UNPKG

23.3 kBJavaScriptView Raw
1/**
2 * Copyright 2013-2015, Facebook, Inc.
3 * All rights reserved.
4 *
5 * This source code is licensed under the BSD-style license found in the
6 * LICENSE file in the root directory of this source tree. An additional grant
7 * of patent rights can be found in the PATENTS file in the same directory.
8 */
9
10/**
11 * This file resembles what we use for our internal configuration. Several changes
12 * have been made to acoomodate the differences between our internal setup and
13 * what we would expect to see in open source.
14 *
15 * Internally we also lint each file individually, allowing use to use the file
16 * path to selectively enable/disable pieces of the lint configuration. For
17 * example, we don't actually want jest globals to be enabled all the time so
18 * we only enable that when we know we're linting a test file. That isn't possible
19 * here so we just always enable that.
20 *
21 * We are also missing our growing library of custom rules. Many of those will
22 * make their way out here soon, but it does mean we need to do some editing of
23 * our configuration object.
24 */
25
26'use strict';
27
28const shared = require('./shared');
29const maxLenIgnorePattern = shared.maxLenIgnorePattern;
30
31// see http://eslint.org/docs/user-guide/configuring.html#configuring-rules
32const OFF = 0;
33const WARNING = 1;
34const ERROR = 2;
35const INDENT_SIZE = 2;
36
37function getBaseConfig() {
38 return {
39 parser: 'babel-eslint',
40 parserOptions: {
41 ecmaVersion: 6,
42 sourceType: 'module',
43 },
44
45 plugins: [
46 'babel',
47 'flowtype',
48 'jsx-a11y',
49 'react',
50 'relay',
51 ],
52
53 // Tries to match the jshint configuration as closely as possible, with the
54 // exeception of a few things that jshint doesn't check, but that we really
55 // shouldn't be using anyways.
56 //
57 // Things that jshint checked for are errors, new rules are warnings.
58 //
59 // If you update eslint, be sure to check the changelog to figure out what
60 // rules to add/remove to/from this list.
61 rules: {
62 // Possible Errors <http://eslint.org/docs/rules/#possible-errors>
63
64 // Forked and moved to fb-www/comma-dangle
65 'comma-dangle': OFF,
66 // equivalent to jshint boss
67 'no-cond-assign': OFF,
68 // equivalent to jshint devel
69 'no-console': [WARNING, {
70 allow: ['warn', 'error', 'time', 'timeEnd', 'timeStamp'],
71 }],
72 // prohibits things like `while (true)`
73 'no-constant-condition': OFF,
74 // we need to be able to match these
75 'no-control-regex': OFF,
76 // equivalent to jshint debug
77 'no-debugger': ERROR,
78 // equivalent to jshint W004
79 'no-dupe-args': ERROR,
80 // syntax error in strict mode, almost certainly unintended in any case
81 'no-dupe-keys': ERROR,
82 // almost certainly a bug
83 'no-duplicate-case': WARNING,
84 // almost certainly a bug
85 'no-empty-character-class': WARNING,
86 // would warn on uncommented empty `catch (ex) {}` blocks
87 'no-empty': OFF,
88 // can cause subtle bugs in IE 8, and we shouldn't do this anyways
89 'no-ex-assign': WARNING,
90 // we shouldn't do this anyways
91 'no-extra-boolean-cast': WARNING,
92 // parens may be used to improve clarity, equivalent to jshint W068
93 'no-extra-parens': [WARNING, 'functions'],
94 // equivalent to jshint W032
95 'no-extra-semi': WARNING,
96 // a function delaration shouldn't be rewritable
97 'no-func-assign': ERROR,
98 // babel and es6 allow block-scoped functions
99 'no-inner-declarations': OFF,
100 // will cause a runtime error
101 'no-invalid-regexp': WARNING,
102 // disallow non-space or tab whitespace characters
103 'no-irregular-whitespace': WARNING,
104 // write `if (!(a in b))`, not `if (!a in b)`, equivalent to jshint W007
105 'no-negated-in-lhs': ERROR,
106 // will cause a runtime error
107 'no-obj-calls': ERROR,
108 // improves legibility
109 'no-regex-spaces': WARNING,
110 // equivalent to jshint elision
111 'no-sparse-arrays': ERROR,
112 // equivalent to jshint W027
113 'no-unreachable': ERROR,
114 // equivalent to jshint use-isnan
115 'use-isnan': ERROR,
116 // probably too noisy ATM
117 'valid-jsdoc': OFF,
118 // equivalent to jshint notypeof
119 'valid-typeof': ERROR,
120 // we already require semicolons
121 'no-unexpected-multiline': OFF,
122
123 // Best Practices <http://eslint.org/docs/rules/#best-practices>
124
125 // probably a bug, we shouldn't actually even use this yet, because of IE8
126 'accessor-pairs': [WARNING, {setWithoutGet: true}],
127 // probably too noisy ATM
128 'block-scoped-var': OFF,
129 // cyclomatic complexity, we're too far gone
130 'complexity': OFF,
131 // require return statements to either always or never specify values
132 'consistent-return': WARNING,
133 // style guide: Always use brackets, even when optional.
134 'curly': [WARNING, 'all'],
135 // we don't do this/care about this
136 'default-case': OFF,
137 // disabled in favor of our temporary fork
138 'dot-notation': OFF,
139 // we don't do this/care about this, but probably should eventually
140 'dot-location': OFF,
141 // disabled as it's too noisy ATM
142 'eqeqeq': [OFF, 'allow-null'],
143 // we don't do this/care about this, equivalent to jshint forin
144 'guard-for-in': OFF,
145 // we have too many internal examples/tools using this
146 'no-alert': OFF,
147 // incompatible with 'use strict' equivalent to jshint noarg
148 'no-caller': ERROR,
149 // we don't care about this right now, but might later
150 'no-case-declarations': OFF,
151 // we don't do this/care about this
152 'no-div-regex': OFF,
153 // we don't do this/care about this
154 'no-else-return': OFF,
155 // avoid mistaken variables when destructuring
156 'no-empty-pattern': WARNING,
157 // see eqeqeq: we explicitly allow this, equivalent to jshint eqnull
158 'no-eq-null': OFF,
159 // equivalent to jshint evil
160 'no-eval': ERROR,
161 // should only be triggered on polyfills, which we can fix case-by-case
162 'no-extend-native': WARNING,
163 // might be a sign of a bug
164 'no-extra-bind': WARNING,
165 // equivalent to jshint W089
166 'no-fallthrough': WARNING,
167 // equivalent to jshint W008
168 'no-floating-decimal': ERROR,
169 // implicit coercion is often idiomatic
170 'no-implicit-coercion': OFF,
171 // equivalent to jshint evil/W066
172 'no-implied-eval': ERROR,
173 // will likely create more signal than noise
174 'no-invalid-this': OFF,
175 // babel should handle this fine
176 'no-iterator': OFF,
177 // Should be effectively equivalent to jshint W028 - allowing the use
178 // of labels in very specific situations. ESLint no-empty-labels was
179 // deprecated.
180 'no-labels': [ERROR, {allowLoop: true, allowSwitch: true}],
181 // lone blocks create no scope, will ignore blocks with let/const
182 'no-lone-blocks': WARNING,
183 // equivalent to jshint loopfunc
184 'no-loop-func': OFF,
185 // we surely have these, don't bother with it
186 'no-magic-numbers': OFF,
187 // we may use this for alignment in some places
188 'no-multi-spaces': OFF,
189 // equivalent to jshint multistr, consider using es6 template strings
190 'no-multi-str': ERROR,
191 // equivalent to jshint W02OFF, similar to no-extend-native
192 'no-native-reassign': [ERROR, {exceptions: ['Map', 'Set']}],
193 // equivalent to jshint evil/W054
194 'no-new-func': ERROR,
195 // don't use constructors for side-effects, equivalent to jshint nonew
196 'no-new': WARNING,
197 // very limited uses, mostly in third_party
198 'no-new-wrappers': WARNING,
199 // deprecated in ES5, but we still use it in some places
200 'no-octal-escape': WARNING,
201 // deprecated in ES5, may cause unexpected behavior
202 'no-octal': WARNING,
203 // treats function parameters as constants, probably too noisy ATM
204 'no-param-reassign': OFF,
205 // only relevant to node code
206 'no-process-env': OFF,
207 // deprecated in ES3.WARNING, equivalent to jshint proto
208 'no-proto': ERROR,
209 // jshint doesn't catch this, but this is inexcusable
210 'no-redeclare': WARNING,
211 // equivalent to jshint boss
212 'no-return-assign': OFF,
213 // equivalent to jshint scripturl
214 'no-script-url': ERROR,
215 // not in jshint, but is in jslint, and is almost certainly a mistake
216 'no-self-compare': WARNING,
217 // there are very limited valid use-cases for this
218 'no-sequences': WARNING,
219 // we're already pretty good about this, and it hides stack traces
220 'no-throw-literal': ERROR,
221 // breaks on `foo && foo.bar()` expression statements, which are common
222 'no-unused-expressions': OFF,
223 // disallow unnecessary .call() and .apply()
224 'no-useless-call': WARNING,
225 // disallow concatenating string literals
226 'no-useless-concat': WARNING,
227 // this has valid use-cases, eg. to circumvent no-unused-expressions
228 'no-void': OFF,
229 // this journey is 1% finished (allow TODO comments)
230 'no-warning-comments': OFF,
231 // equivalent to jshint withstmt
232 'no-with': OFF,
233 // require radix argument in parseInt, we do this in most places already
234 'radix': WARNING,
235 // we don't do this/care about this
236 'vars-on-top': OFF,
237 // equivalent to jshint immed
238 'wrap-iife': OFF,
239 // probably too noisy ATM
240 'yoda': OFF,
241
242 // Strict Mode <http://eslint.org/docs/rules/#strict-mode>
243 // jshint wasn't checking this, and the compiler should add this anyways
244 'strict': OFF,
245
246 // Variables <http://eslint.org/docs/rules/#variables>
247 // we don't do this/care about this
248 'init-declarations': OFF,
249 // equivalent to jshint W002, catches an IE8 bug
250 'no-catch-shadow': ERROR,
251 // equivalent to jshint W051, is a strict mode violation
252 'no-delete-var': ERROR,
253 // we should avoid labels anyways
254 'no-label-var': WARNING,
255 // redefining undefined, NaN, Infinity, arguments, and eval is bad, mkay?
256 'no-shadow-restricted-names': WARNING,
257 // a definite code-smell, but probably too noisy
258 'no-shadow': OFF,
259 // it's nice to be explicit sometimes: `let foo = undefined;`
260 'no-undef-init': OFF,
261 // equivalent to jshint undef, turned into an error in getConfig
262 'no-undef': WARNING,
263 // using undefined is safe because we enforce no-shadow-restricted-names
264 'no-undefined': OFF,
265 // equivalent to jshint unused
266 'no-unused-vars': [WARNING, {args: 'none', varsIgnorePattern: '^_'}],
267 // too noisy
268 'no-use-before-define': OFF,
269
270 // Node.js <http://eslint.org/docs/rules/#nodejs>
271 // TODO: turn some of these on in places where we lint node code
272 'callback-return': OFF,
273 'global-require': OFF,
274 'handle-callback-err': OFF,
275 'no-mixed-requires': OFF,
276 'no-new-require': OFF,
277 'no-path-concat': OFF,
278 'no-process-exit': OFF,
279 'no-restricted-modules': OFF,
280 'no-sync': OFF,
281
282 // Stylistic Issues <http://eslint.org/docs/rules/#stylistic-issues>
283 // See also: https://our.intern.facebook.com/intern/dex/style-guide/
284 'array-bracket-spacing': WARNING,
285 // TODO: enable this with consensus on single line blocks
286 'block-spacing': OFF,
287 'brace-style': [WARNING, '1tbs', {allowSingleLine: true}],
288 // too noisy at the moment, and jshint didn't check it
289 'camelcase': [OFF, {properties: 'always'}],
290 'comma-spacing': [WARNING, {before: false, after: true}],
291 // jshint had laxcomma, but that was against our style guide
292 'comma-style': [WARNING, 'last'],
293 'computed-property-spacing': [WARNING, 'never'],
294 // we may use more contextually relevant names for this than self
295 'consistent-this': [OFF, 'self'],
296 // should be handled by a generic TXT linter instead
297 'eol-last': OFF,
298 'func-names': OFF,
299 // too noisy ATM
300 'func-style': [OFF, 'declaration'],
301 // no way we could enforce min/max lengths or patterns for vars
302 'id-length': OFF,
303 'id-match': OFF,
304 // we weren't enforcing this with jshint, so erroring would be too noisy
305 'indent': [WARNING, INDENT_SIZE, {SwitchCase: 1}],
306 // we use single quotes for JS literals, double quotes for JSX literals
307 'jsx-quotes': [WARNING, 'prefer-double'],
308 // we may use extra spaces for alignment
309 'key-spacing': [OFF, {beforeColon: false, afterColon: true}],
310 'keyword-spacing': [WARNING],
311 'lines-around-comment': OFF,
312 // should be handled by a generic TXT linter instead
313 'linebreak-style': [OFF, 'unix'],
314 'max-depth': OFF,
315 'max-len': [WARNING, 80, INDENT_SIZE,
316 {'ignorePattern': maxLenIgnorePattern, 'ignoreUrls': true},
317 ],
318 'max-nested-callbacks': OFF,
319 'max-params': OFF,
320 'max-statements': OFF,
321 // https://facebook.com/groups/995898333776940/1027358627297577
322 'new-cap': OFF,
323 // equivalent to jshint W058
324 'new-parens': ERROR,
325 'newline-after-var': OFF,
326 'no-array-constructor': ERROR,
327 'no-bitwise': WARNING,
328 'no-continue': OFF,
329 'no-inline-comments': OFF,
330 // doesn't play well with `if (__DEV__) {}`
331 'no-lonely-if': OFF,
332 // stopgap, irrelevant if we can eventually turn `indent` on to error
333 'no-mixed-spaces-and-tabs': ERROR,
334 // don't care
335 'no-multiple-empty-lines': OFF,
336 'no-negated-condition': OFF,
337 // we do this a bunch of places, and it's less bad with proper indentation
338 'no-nested-ternary': OFF,
339 // similar to FacebookWebJSLintLinter's checkPhpStyleArray
340 'no-new-object': WARNING,
341 'no-plusplus': OFF,
342 'no-restricted-syntax': OFF,
343 'no-spaced-func': WARNING,
344 'no-ternary': OFF,
345 // should be handled by a generic TXT linter instead
346 'no-trailing-spaces': OFF,
347 // we use this for private/protected identifiers
348 'no-underscore-dangle': OFF,
349 // disallow `let isYes = answer === 1 ? true : false;`
350 'no-unneeded-ternary': WARNING,
351 // too noisy ATM
352 'object-curly-spacing': OFF,
353 // makes indentation warnings clearer
354 'one-var': [WARNING, {initialized: 'never'}],
355 // prefer `x += 4` over `x = x + 4`
356 'operator-assignment': [WARNING, 'always'],
357 // equivalent to jshint laxbreak
358 'operator-linebreak': OFF,
359 'padded-blocks': OFF,
360 // probably too noisy on pre-ES5 code
361 'quote-props': [OFF, 'as-needed'],
362 'quotes': [
363 WARNING,
364 'single',
365 {
366 avoidEscape: true,
367 allowTemplateLiterals: true,
368 },
369 ],
370 'require-jsdoc': OFF,
371 'semi-spacing': [WARNING, {before: false, after: true}],
372 // equivalent to jshint asi/W032
373 'semi': [WARNING, 'always'],
374 'sort-vars': OFF,
375 // require `if () {` instead of `if (){`
376 'space-before-blocks': [WARNING, 'always'],
377 // require `function foo()` instead of `function foo ()`
378 'space-before-function-paren': [
379 WARNING,
380 {anonymous: 'never', named: 'never'},
381 ],
382 // incompatible with our legacy inline type annotations
383 'space-in-parens': [OFF, 'never'],
384 'space-infix-ops': [WARNING, {int32Hint: true}],
385 'space-unary-ops': [WARNING, {words: true, nonwords: false}],
386 // TODO: Figure out a way to do this that doesn't break typechecks
387 // or wait for https://github.com/eslint/eslint/issues/2897
388 'spaced-comment':
389 [OFF, 'always', {exceptions: ['jshint', 'jslint', 'eslint', 'global']}],
390 'wrap-regex': OFF,
391
392 // ECMAScript 6 <http://eslint.org/docs/rules/#ecmascript-6>
393 'arrow-body-style': OFF,
394 // Forked to fb-www/arrow-parens to fix issues with flow and add fixer
395 'arrow-parens': OFF,
396 // tbgs finds *very few* places where we don't put spaces around =>
397 'arrow-spacing': [WARNING, {before: true, after: true}],
398 // violation of the ES6 spec, won't transform
399 'constructor-super': ERROR,
400 // https://github.com/babel/babel-eslint#known-issues
401 'generator-star-spacing': OFF,
402 'no-class-assign': WARNING,
403 'no-confusing-arrow': OFF,
404 // this is a runtime error
405 'no-const-assign': ERROR,
406 'no-dupe-class-members': ERROR,
407 // violation of the ES6 spec, won't transform, `this` is part of the TDZ
408 'no-this-before-super': ERROR,
409 'no-useless-computed-key': WARNING,
410 // we have way too much ES3 & ES5 code
411 'no-var': OFF,
412 'object-shorthand': OFF,
413 'prefer-const': OFF,
414 'prefer-spread': OFF,
415 // we don't support/polyfill this yet
416 'prefer-reflect': OFF,
417 'prefer-template': OFF,
418 // there are legitimate use-cases for an empty generator
419 'require-yield': OFF,
420
421 // eslint-plugin-babel <https://github.com/babel/eslint-plugin-babel>
422 'babel/generator-star-spacing': OFF,
423 'babel/new-cap': OFF,
424 'babel/array-bracket-spacing': OFF,
425 'babel/object-curly-spacing': OFF,
426 'babel/object-shorthand': OFF,
427 'babel/arrow-parens': OFF,
428 'babel/no-await-in-loop': OFF,
429 'babel/flow-object-type': [WARNING, 'comma'],
430
431 // eslint-plugin-react <https://github.com/yannickcr/eslint-plugin-react>
432 // TODO: We're being extremely conservative here as we roll out eslint on
433 // www. As we finish rollout, we can turn on more of these, and replace
434 // some legacy regex rules in the process.
435 'react/display-name': OFF,
436 'react/forbid-prop-types': OFF,
437 'react/jsx-boolean-value': OFF,
438 'react/jsx-closing-bracket-location': OFF,
439 'react/jsx-curly-spacing': OFF,
440 'react/jsx-equals-spacing': WARNING,
441 'react/jsx-filename-extension': OFF,
442 'react/jsx-first-prop-new-line': OFF,
443 'react/jsx-handler-names': OFF,
444 'react/jsx-indent': OFF,
445 'react/jsx-indent-props': OFF,
446 'react/jsx-key': OFF,
447 'react/jsx-max-props-per-line': OFF,
448 'react/jsx-no-bind': OFF,
449 'react/jsx-no-duplicate-props': ERROR,
450 'react/jsx-no-literals': OFF,
451 'react/jsx-no-target-blank': OFF,
452 'react/jsx-no-undef': ERROR,
453 'react/jsx-pascal-case': OFF,
454 'react/jsx-sort-props': OFF,
455 'react/jsx-space-before-closing': OFF,
456 // forked to fb-www/jsx-uses-react
457 'react/jsx-uses-react': OFF,
458 'react/jsx-uses-vars': ERROR,
459 'react/jsx-wrap-multilines': OFF,
460 'react/no-comment-textnodes': OFF,
461 'react/no-danger': OFF,
462 'react/no-deprecated': OFF,
463 'react/no-did-mount-set-state': OFF,
464 'react/no-did-update-set-state': OFF,
465 'react/no-direct-mutation-state': OFF,
466 'react/no-is-mounted': WARNING,
467 'react/no-multi-comp': OFF,
468 'react/no-render-return-value': OFF,
469 'react/no-set-state': OFF,
470 'react/no-string-refs': OFF,
471 'react/no-unknown-property': OFF,
472 'react/prefer-es6-class': OFF,
473 'react/prefer-stateless-function': OFF,
474 'react/prop-types': OFF,
475 // forked to fb-www/react-in-jsx-scope
476 'react/react-in-jsx-scope': OFF,
477 'react/require-extension': OFF,
478 'react/require-optimization': OFF,
479 'react/require-render-return': OFF,
480 'react/self-closing-comp': OFF,
481 'react/sort-comp': OFF,
482 'react/sort-prop-types': OFF,
483
484 // JSX Accessibility checks
485 'jsx-a11y/accessible-emoji': OFF,
486 'jsx-a11y/anchor-has-content': OFF,
487 'jsx-a11y/aria-activedescendant-has-tabindex': OFF,
488 'jsx-a11y/aria-props': WARNING,
489 'jsx-a11y/aria-proptypes': OFF,
490 'jsx-a11y/aria-role': WARNING,
491 'jsx-a11y/aria-unsupported-elements': OFF,
492 'jsx-a11y/click-events-have-key-events': OFF,
493 'jsx-a11y/heading-has-content': OFF,
494 'jsx-a11y/html-has-lang': OFF,
495 'jsx-a11y/iframe-has-title': OFF,
496 'jsx-a11y/img-has-alt': OFF,
497 'jsx-a11y/img-redundant-alt': OFF,
498 'jsx-a11y/interactive-supports-focus': [
499 WARNING,
500 {
501 tabbable: [
502 'button',
503 'checkbox',
504 'link',
505 'searchbox',
506 'spinbutton',
507 'switch',
508 'textbox',
509 ],
510 },
511 ],
512 'jsx-a11y/label-has-for': OFF,
513 'jsx-a11y/lang': OFF,
514 'jsx-a11y/mouse-events-have-key-events': OFF,
515 'jsx-a11y/no-access-key': OFF,
516 'jsx-a11y/no-autofocus': OFF,
517 'jsx-a11y/no-distracting-elements': OFF,
518 'jsx-a11y/no-interactive-element-to-noninteractive-role': [
519 WARNING,
520 {
521 tr: ['none', 'presentation'],
522 },
523 ],
524 'jsx-a11y/no-noninteractive-element-interactions': [
525 WARNING,
526 {
527 handlers: ['onClick'],
528 },
529 ],
530 'jsx-a11y/no-noninteractive-element-to-interactive-role': [
531 WARNING,
532 {
533 ul: ['listbox', 'menu', 'menubar',
534 'radiogroup', 'tablist', 'tree', 'treegrid'],
535 ol: ['listbox', 'menu', 'menubar',
536 'radiogroup', 'tablist', 'tree', 'treegrid'],
537 li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
538 table: ['grid'],
539 td: ['gridcell'],
540 },
541 ],
542 'jsx-a11y/no-noninteractive-tabindex': WARNING,
543 'jsx-a11y/no-onchange': OFF,
544 'jsx-a11y/no-redundant-roles': OFF,
545 'jsx-a11y/no-static-element-interactions': [
546 WARNING,
547 {
548 handlers: ['onClick'],
549 },
550 ],
551 'jsx-a11y/role-has-required-aria-props': WARNING,
552 'jsx-a11y/role-supports-aria-props': WARNING,
553 'jsx-a11y/scope': OFF,
554 'jsx-a11y/tabindex-no-positive': WARNING,
555
556 // eslint-plugin-flowtype
557 // These don't actually result in warnings. Enabling them ensures they run
558 // and mark variables as used, avoiding false positives with Flow
559 // annotations.
560 'flowtype/define-flow-type': WARNING,
561 'flowtype/use-flow-type': WARNING,
562
563
564 'relay/graphql-syntax': ERROR,
565 'relay/graphql-naming': ERROR,
566 'relay/compat-uses-vars': WARNING,
567 },
568
569 // Defines a basic set of globals
570 env: {
571 browser: true,
572 es6: true,
573 },
574
575 globals: shared.globals,
576
577 };
578}
579
580// Override some rules for open source. Due to the way we apply our configuation
581// internally, these are effectively part of the same configuration we apply.
582var config = getBaseConfig();
583var extendedConfig = {
584 env: {
585 // Enable these blindly because we can't make a per-file decision about this.
586 node: true,
587 jest: true,
588 jasmine: true,
589 },
590 rules: {
591 // just turned into an error here since we almost always do that anyway.
592 'no-undef': ERROR,
593
594 // Re-enable some forked rules. Good enough for open source
595 'comma-dangle': [WARNING, 'always-multiline'],
596
597 'react/jsx-uses-react': ERROR,
598 'react/react-in-jsx-scope': ERROR,
599
600 // To keep base config in sync with internal codebase but still make
601 // open source happy, disable a deprecated rule and enable different one.
602 'babel/flow-object-type': OFF,
603 'flowtype/object-type-delimiter': [WARNING, 'comma'],
604 },
605};
606
607Object.keys(extendedConfig).forEach((key) => {
608 config[key] = Object.assign(config[key], extendedConfig[key]);
609});
610
611module.exports = config;