eslint 8.4.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,709 +0,0 @@
1
- /**
2
- * @fileoverview Config initialization wizard.
3
- * @author Ilya Volodin
4
- */
5
-
6
-
7
- "use strict";
8
-
9
- //------------------------------------------------------------------------------
10
- // Requirements
11
- //------------------------------------------------------------------------------
12
-
13
- const util = require("util"),
14
- path = require("path"),
15
- fs = require("fs"),
16
- enquirer = require("enquirer"),
17
- ProgressBar = require("progress"),
18
- semver = require("semver"),
19
- espree = require("espree"),
20
- recConfig = require("../../conf/eslint-recommended"),
21
- {
22
- Legacy: {
23
- ConfigOps,
24
- naming
25
- }
26
- } = require("@eslint/eslintrc"),
27
- log = require("../shared/logging"),
28
- ModuleResolver = require("../shared/relative-module-resolver"),
29
- autoconfig = require("./autoconfig.js"),
30
- ConfigFile = require("./config-file"),
31
- npmUtils = require("./npm-utils"),
32
- { getSourceCodeOfFiles } = require("./source-code-utils");
33
-
34
- const debug = require("debug")("eslint:config-initializer");
35
-
36
- //------------------------------------------------------------------------------
37
- // Private
38
- //------------------------------------------------------------------------------
39
-
40
- /* istanbul ignore next: hard to test fs function */
41
- /**
42
- * Create .eslintrc file in the current working directory
43
- * @param {Object} config object that contains user's answers
44
- * @param {string} format The file format to write to.
45
- * @returns {void}
46
- */
47
- function writeFile(config, format) {
48
-
49
- // default is .js
50
- let extname = ".js";
51
-
52
- if (format === "YAML") {
53
- extname = ".yml";
54
- } else if (format === "JSON") {
55
- extname = ".json";
56
- } else if (format === "JavaScript") {
57
- const pkgJSONPath = npmUtils.findPackageJson();
58
-
59
- if (pkgJSONPath) {
60
- const pkgJSONContents = JSON.parse(fs.readFileSync(pkgJSONPath, "utf8"));
61
-
62
- if (pkgJSONContents.type === "module") {
63
- extname = ".cjs";
64
- }
65
- }
66
- }
67
-
68
- const installedESLint = config.installedESLint;
69
-
70
- delete config.installedESLint;
71
-
72
- ConfigFile.write(config, `./.eslintrc${extname}`);
73
- log.info(`Successfully created .eslintrc${extname} file in ${process.cwd()}`);
74
-
75
- if (installedESLint) {
76
- log.info("ESLint was installed locally. We recommend using this local copy instead of your globally-installed copy.");
77
- }
78
- }
79
-
80
- /**
81
- * Get the peer dependencies of the given module.
82
- * This adds the gotten value to cache at the first time, then reuses it.
83
- * In a process, this function is called twice, but `npmUtils.fetchPeerDependencies` needs to access network which is relatively slow.
84
- * @param {string} moduleName The module name to get.
85
- * @returns {Object} The peer dependencies of the given module.
86
- * This object is the object of `peerDependencies` field of `package.json`.
87
- * Returns null if npm was not found.
88
- */
89
- function getPeerDependencies(moduleName) {
90
- let result = getPeerDependencies.cache.get(moduleName);
91
-
92
- if (!result) {
93
- log.info(`Checking peerDependencies of ${moduleName}`);
94
-
95
- result = npmUtils.fetchPeerDependencies(moduleName);
96
- getPeerDependencies.cache.set(moduleName, result);
97
- }
98
-
99
- return result;
100
- }
101
- getPeerDependencies.cache = new Map();
102
-
103
- /**
104
- * Return necessary plugins, configs, parsers, etc. based on the config
105
- * @param {Object} config config object
106
- * @param {boolean} [installESLint=true] If `false` is given, it does not install eslint.
107
- * @returns {string[]} An array of modules to be installed.
108
- */
109
- function getModulesList(config, installESLint) {
110
- const modules = {};
111
-
112
- // Create a list of modules which should be installed based on config
113
- if (config.plugins) {
114
- for (const plugin of config.plugins) {
115
- const moduleName = naming.normalizePackageName(plugin, "eslint-plugin");
116
-
117
- modules[moduleName] = "latest";
118
- }
119
- }
120
- if (config.extends) {
121
- const extendList = Array.isArray(config.extends) ? config.extends : [config.extends];
122
-
123
- for (const extend of extendList) {
124
- if (extend.startsWith("eslint:") || extend.startsWith("plugin:")) {
125
- continue;
126
- }
127
- const moduleName = naming.normalizePackageName(extend, "eslint-config");
128
-
129
- modules[moduleName] = "latest";
130
- Object.assign(
131
- modules,
132
- getPeerDependencies(`${moduleName}@latest`)
133
- );
134
- }
135
- }
136
-
137
- const parser = config.parser || (config.parserOptions && config.parserOptions.parser);
138
-
139
- if (parser) {
140
- modules[parser] = "latest";
141
- }
142
-
143
- if (installESLint === false) {
144
- delete modules.eslint;
145
- } else {
146
- const installStatus = npmUtils.checkDevDeps(["eslint"]);
147
-
148
- // Mark to show messages if it's new installation of eslint.
149
- if (installStatus.eslint === false) {
150
- log.info("Local ESLint installation not found.");
151
- modules.eslint = modules.eslint || "latest";
152
- config.installedESLint = true;
153
- }
154
- }
155
-
156
- return Object.keys(modules).map(name => `${name}@${modules[name]}`);
157
- }
158
-
159
- /**
160
- * Set the `rules` of a config by examining a user's source code
161
- *
162
- * Note: This clones the config object and returns a new config to avoid mutating
163
- * the original config parameter.
164
- * @param {Object} answers answers received from enquirer
165
- * @param {Object} config config object
166
- * @throws {Error} If source code retrieval fails or source code file count is 0.
167
- * @returns {Object} config object with configured rules
168
- */
169
- function configureRules(answers, config) {
170
- const BAR_TOTAL = 20,
171
- BAR_SOURCE_CODE_TOTAL = 4,
172
- newConfig = Object.assign({}, config),
173
- disabledConfigs = {};
174
- let sourceCodes,
175
- registry;
176
-
177
- // Set up a progress bar, as this process can take a long time
178
- const bar = new ProgressBar("Determining Config: :percent [:bar] :elapseds elapsed, eta :etas ", {
179
- width: 30,
180
- total: BAR_TOTAL
181
- });
182
-
183
- bar.tick(0); // Shows the progress bar
184
-
185
- // Get the SourceCode of all chosen files
186
- const patterns = answers.patterns.split(/[\s]+/u);
187
-
188
- try {
189
- sourceCodes = getSourceCodeOfFiles(patterns, { baseConfig: newConfig, useEslintrc: false }, total => {
190
- bar.tick((BAR_SOURCE_CODE_TOTAL / total));
191
- });
192
- } catch (e) {
193
- log.info("\n");
194
- throw e;
195
- }
196
- const fileQty = Object.keys(sourceCodes).length;
197
-
198
- if (fileQty === 0) {
199
- log.info("\n");
200
- throw new Error("Automatic Configuration failed. No files were able to be parsed.");
201
- }
202
-
203
- // Create a registry of rule configs
204
- registry = new autoconfig.Registry();
205
- registry.populateFromCoreRules();
206
-
207
- // Lint all files with each rule config in the registry
208
- registry = registry.lintSourceCode(sourceCodes, newConfig, total => {
209
- bar.tick((BAR_TOTAL - BAR_SOURCE_CODE_TOTAL) / total); // Subtract out ticks used at beginning
210
- });
211
- debug(`\nRegistry: ${util.inspect(registry.rules, { depth: null })}`);
212
-
213
- // Create a list of recommended rules, because we don't want to disable them
214
- const recRules = Object.keys(recConfig.rules).filter(ruleId => ConfigOps.isErrorSeverity(recConfig.rules[ruleId]));
215
-
216
- // Find and disable rules which had no error-free configuration
217
- const failingRegistry = registry.getFailingRulesRegistry();
218
-
219
- Object.keys(failingRegistry.rules).forEach(ruleId => {
220
-
221
- // If the rule is recommended, set it to error, otherwise disable it
222
- disabledConfigs[ruleId] = (recRules.indexOf(ruleId) !== -1) ? 2 : 0;
223
- });
224
-
225
- // Now that we know which rules to disable, strip out configs with errors
226
- registry = registry.stripFailingConfigs();
227
-
228
- /*
229
- * If there is only one config that results in no errors for a rule, we should use it.
230
- * createConfig will only add rules that have one configuration in the registry.
231
- */
232
- const singleConfigs = registry.createConfig().rules;
233
-
234
- /*
235
- * The "sweet spot" for number of options in a config seems to be two (severity plus one option).
236
- * Very often, a third option (usually an object) is available to address
237
- * edge cases, exceptions, or unique situations. We will prefer to use a config with
238
- * specificity of two.
239
- */
240
- const specTwoConfigs = registry.filterBySpecificity(2).createConfig().rules;
241
-
242
- // Maybe a specific combination using all three options works
243
- const specThreeConfigs = registry.filterBySpecificity(3).createConfig().rules;
244
-
245
- // If all else fails, try to use the default (severity only)
246
- const defaultConfigs = registry.filterBySpecificity(1).createConfig().rules;
247
-
248
- // Combine configs in reverse priority order (later take precedence)
249
- newConfig.rules = Object.assign({}, disabledConfigs, defaultConfigs, specThreeConfigs, specTwoConfigs, singleConfigs);
250
-
251
- // Make sure progress bar has finished (floating point rounding)
252
- bar.update(BAR_TOTAL);
253
-
254
- // Log out some stats to let the user know what happened
255
- const finalRuleIds = Object.keys(newConfig.rules);
256
- const totalRules = finalRuleIds.length;
257
- const enabledRules = finalRuleIds.filter(ruleId => (newConfig.rules[ruleId] !== 0)).length;
258
- const resultMessage = [
259
- `\nEnabled ${enabledRules} out of ${totalRules}`,
260
- `rules based on ${fileQty}`,
261
- `file${(fileQty === 1) ? "." : "s."}`
262
- ].join(" ");
263
-
264
- log.info(resultMessage);
265
-
266
- ConfigOps.normalizeToStrings(newConfig);
267
- return newConfig;
268
- }
269
-
270
- /**
271
- * process user's answers and create config object
272
- * @param {Object} answers answers received from enquirer
273
- * @returns {Object} config object
274
- */
275
- function processAnswers(answers) {
276
- let config = {
277
- rules: {},
278
- env: {},
279
- parserOptions: {},
280
- extends: []
281
- };
282
-
283
- config.parserOptions.ecmaVersion = espree.latestEcmaVersion;
284
- config.env.es2021 = true;
285
-
286
- // set the module type
287
- if (answers.moduleType === "esm") {
288
- config.parserOptions.sourceType = "module";
289
- } else if (answers.moduleType === "commonjs") {
290
- config.env.commonjs = true;
291
- }
292
-
293
- // add in browser and node environments if necessary
294
- answers.env.forEach(env => {
295
- config.env[env] = true;
296
- });
297
-
298
- // add in library information
299
- if (answers.framework === "react") {
300
- config.parserOptions.ecmaFeatures = {
301
- jsx: true
302
- };
303
- config.plugins = ["react"];
304
- config.extends.push("plugin:react/recommended");
305
- } else if (answers.framework === "vue") {
306
- config.plugins = ["vue"];
307
- config.extends.push("plugin:vue/essential");
308
- }
309
-
310
- if (answers.typescript) {
311
- if (answers.framework === "vue") {
312
- config.parserOptions.parser = "@typescript-eslint/parser";
313
- } else {
314
- config.parser = "@typescript-eslint/parser";
315
- }
316
-
317
- if (Array.isArray(config.plugins)) {
318
- config.plugins.push("@typescript-eslint");
319
- } else {
320
- config.plugins = ["@typescript-eslint"];
321
- }
322
- }
323
-
324
- // setup rules based on problems/style enforcement preferences
325
- if (answers.purpose === "problems") {
326
- config.extends.unshift("eslint:recommended");
327
- } else if (answers.purpose === "style") {
328
- if (answers.source === "prompt") {
329
- config.extends.unshift("eslint:recommended");
330
- config.rules.indent = ["error", answers.indent];
331
- config.rules.quotes = ["error", answers.quotes];
332
- config.rules["linebreak-style"] = ["error", answers.linebreak];
333
- config.rules.semi = ["error", answers.semi ? "always" : "never"];
334
- } else if (answers.source === "auto") {
335
- config = configureRules(answers, config);
336
- config = autoconfig.extendFromRecommended(config);
337
- }
338
- }
339
- if (answers.typescript && config.extends.includes("eslint:recommended")) {
340
- config.extends.push("plugin:@typescript-eslint/recommended");
341
- }
342
-
343
- // normalize extends
344
- if (config.extends.length === 0) {
345
- delete config.extends;
346
- } else if (config.extends.length === 1) {
347
- config.extends = config.extends[0];
348
- }
349
-
350
- ConfigOps.normalizeToStrings(config);
351
- return config;
352
- }
353
-
354
- /**
355
- * Get the version of the local ESLint.
356
- * @returns {string|null} The version. If the local ESLint was not found, returns null.
357
- */
358
- function getLocalESLintVersion() {
359
- try {
360
- const eslintPath = ModuleResolver.resolve("eslint", path.join(process.cwd(), "__placeholder__.js"));
361
- const eslint = require(eslintPath);
362
-
363
- return eslint.linter.version || null;
364
- } catch {
365
- return null;
366
- }
367
- }
368
-
369
- /**
370
- * Get the shareable config name of the chosen style guide.
371
- * @param {Object} answers The answers object.
372
- * @returns {string} The shareable config name.
373
- */
374
- function getStyleGuideName(answers) {
375
- if (answers.styleguide === "airbnb" && answers.framework !== "react") {
376
- return "airbnb-base";
377
- }
378
- return answers.styleguide;
379
- }
380
-
381
- /**
382
- * Check whether the local ESLint version conflicts with the required version of the chosen shareable config.
383
- * @param {Object} answers The answers object.
384
- * @returns {boolean} `true` if the local ESLint is found then it conflicts with the required version of the chosen shareable config.
385
- */
386
- function hasESLintVersionConflict(answers) {
387
-
388
- // Get the local ESLint version.
389
- const localESLintVersion = getLocalESLintVersion();
390
-
391
- if (!localESLintVersion) {
392
- return false;
393
- }
394
-
395
- // Get the required range of ESLint version.
396
- const configName = getStyleGuideName(answers);
397
- const moduleName = `eslint-config-${configName}@latest`;
398
- const peerDependencies = getPeerDependencies(moduleName) || {};
399
- const requiredESLintVersionRange = peerDependencies.eslint;
400
-
401
- if (!requiredESLintVersionRange) {
402
- return false;
403
- }
404
-
405
- answers.localESLintVersion = localESLintVersion;
406
- answers.requiredESLintVersionRange = requiredESLintVersionRange;
407
-
408
- // Check the version.
409
- if (semver.satisfies(localESLintVersion, requiredESLintVersionRange)) {
410
- answers.installESLint = false;
411
- return false;
412
- }
413
-
414
- return true;
415
- }
416
-
417
- /**
418
- * Install modules.
419
- * @param {string[]} modules Modules to be installed.
420
- * @returns {void}
421
- */
422
- function installModules(modules) {
423
- log.info(`Installing ${modules.join(", ")}`);
424
- npmUtils.installSyncSaveDev(modules);
425
- }
426
-
427
- /* istanbul ignore next: no need to test enquirer */
428
- /**
429
- * Ask user to install modules.
430
- * @param {string[]} modules Array of modules to be installed.
431
- * @param {boolean} packageJsonExists Indicates if package.json is existed.
432
- * @returns {Promise<void>} Answer that indicates if user wants to install.
433
- */
434
- function askInstallModules(modules, packageJsonExists) {
435
-
436
- // If no modules, do nothing.
437
- if (modules.length === 0) {
438
- return Promise.resolve();
439
- }
440
-
441
- log.info("The config that you've selected requires the following dependencies:\n");
442
- log.info(modules.join(" "));
443
- return enquirer.prompt([
444
- {
445
- type: "toggle",
446
- name: "executeInstallation",
447
- message: "Would you like to install them now with npm?",
448
- enabled: "Yes",
449
- disabled: "No",
450
- initial: 1,
451
- skip() {
452
- return !(modules.length && packageJsonExists);
453
- },
454
- result(input) {
455
- return this.skipped ? null : input;
456
- }
457
- }
458
- ]).then(({ executeInstallation }) => {
459
- if (executeInstallation) {
460
- installModules(modules);
461
- }
462
- });
463
- }
464
-
465
- /* istanbul ignore next: no need to test enquirer */
466
- /**
467
- * Ask use a few questions on command prompt
468
- * @returns {Promise<void>} The promise with the result of the prompt
469
- */
470
- function promptUser() {
471
-
472
- return enquirer.prompt([
473
- {
474
- type: "select",
475
- name: "purpose",
476
- message: "How would you like to use ESLint?",
477
-
478
- // The returned number matches the name value of nth in the choices array.
479
- initial: 1,
480
- choices: [
481
- { message: "To check syntax only", name: "syntax" },
482
- { message: "To check syntax and find problems", name: "problems" },
483
- { message: "To check syntax, find problems, and enforce code style", name: "style" }
484
- ]
485
- },
486
- {
487
- type: "select",
488
- name: "moduleType",
489
- message: "What type of modules does your project use?",
490
- initial: 0,
491
- choices: [
492
- { message: "JavaScript modules (import/export)", name: "esm" },
493
- { message: "CommonJS (require/exports)", name: "commonjs" },
494
- { message: "None of these", name: "none" }
495
- ]
496
- },
497
- {
498
- type: "select",
499
- name: "framework",
500
- message: "Which framework does your project use?",
501
- initial: 0,
502
- choices: [
503
- { message: "React", name: "react" },
504
- { message: "Vue.js", name: "vue" },
505
- { message: "None of these", name: "none" }
506
- ]
507
- },
508
- {
509
- type: "toggle",
510
- name: "typescript",
511
- message: "Does your project use TypeScript?",
512
- enabled: "Yes",
513
- disabled: "No",
514
- initial: 0
515
- },
516
- {
517
- type: "multiselect",
518
- name: "env",
519
- message: "Where does your code run?",
520
- hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
521
- initial: 0,
522
- choices: [
523
- { message: "Browser", name: "browser" },
524
- { message: "Node", name: "node" }
525
- ]
526
- },
527
- {
528
- type: "select",
529
- name: "source",
530
- message: "How would you like to define a style for your project?",
531
- choices: [
532
- { message: "Use a popular style guide", name: "guide" },
533
- { message: "Answer questions about your style", name: "prompt" },
534
- { message: "Inspect your JavaScript file(s)", name: "auto" }
535
- ],
536
- skip() {
537
- return this.state.answers.purpose !== "style";
538
- },
539
- result(input) {
540
- return this.skipped ? null : input;
541
- }
542
- },
543
- {
544
- type: "select",
545
- name: "styleguide",
546
- message: "Which style guide do you want to follow?",
547
- choices: [
548
- { message: "Airbnb: https://github.com/airbnb/javascript", name: "airbnb" },
549
- { message: "Standard: https://github.com/standard/standard", name: "standard" },
550
- { message: "Google: https://github.com/google/eslint-config-google", name: "google" },
551
- { message: "XO: https://github.com/xojs/eslint-config-xo", name: "xo" }
552
- ],
553
- skip() {
554
- this.state.answers.packageJsonExists = npmUtils.checkPackageJson();
555
- return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists);
556
- },
557
- result(input) {
558
- return this.skipped ? null : input;
559
- }
560
- },
561
- {
562
- type: "input",
563
- name: "patterns",
564
- message: "Which file(s), path(s), or glob(s) should be examined?",
565
- skip() {
566
- return this.state.answers.source !== "auto";
567
- },
568
- validate(input) {
569
- if (!this.skipped && input.trim().length === 0 && input.trim() !== ",") {
570
- return "You must tell us what code to examine. Try again.";
571
- }
572
- return true;
573
- }
574
- },
575
- {
576
- type: "select",
577
- name: "format",
578
- message: "What format do you want your config file to be in?",
579
- initial: 0,
580
- choices: ["JavaScript", "YAML", "JSON"]
581
- },
582
- {
583
- type: "toggle",
584
- name: "installESLint",
585
- message() {
586
- const { answers } = this.state;
587
- const verb = semver.ltr(answers.localESLintVersion, answers.requiredESLintVersionRange)
588
- ? "upgrade"
589
- : "downgrade";
590
-
591
- return `The style guide "${answers.styleguide}" requires eslint@${answers.requiredESLintVersionRange}. You are currently using eslint@${answers.localESLintVersion}.\n Do you want to ${verb}?`;
592
- },
593
- enabled: "Yes",
594
- disabled: "No",
595
- initial: 1,
596
- skip() {
597
- return !(this.state.answers.source === "guide" && this.state.answers.packageJsonExists && hasESLintVersionConflict(this.state.answers));
598
- },
599
- result(input) {
600
- return this.skipped ? null : input;
601
- }
602
- }
603
- ]).then(earlyAnswers => {
604
-
605
- // early exit if no style guide is necessary
606
- if (earlyAnswers.purpose !== "style") {
607
- const config = processAnswers(earlyAnswers);
608
- const modules = getModulesList(config);
609
-
610
- return askInstallModules(modules, earlyAnswers.packageJsonExists)
611
- .then(() => writeFile(config, earlyAnswers.format));
612
- }
613
-
614
- // early exit if you are using a style guide
615
- if (earlyAnswers.source === "guide") {
616
- if (!earlyAnswers.packageJsonExists) {
617
- log.info("A package.json is necessary to install plugins such as style guides. Run `npm init` to create a package.json file and try again.");
618
- return void 0;
619
- }
620
- if (earlyAnswers.installESLint === false && !semver.satisfies(earlyAnswers.localESLintVersion, earlyAnswers.requiredESLintVersionRange)) {
621
- log.info(`Note: it might not work since ESLint's version is mismatched with the ${earlyAnswers.styleguide} config.`);
622
- }
623
- if (earlyAnswers.styleguide === "airbnb" && earlyAnswers.framework !== "react") {
624
- earlyAnswers.styleguide = "airbnb-base";
625
- }
626
-
627
- const config = processAnswers(earlyAnswers);
628
-
629
- if (Array.isArray(config.extends)) {
630
- config.extends.push(earlyAnswers.styleguide);
631
- } else if (config.extends) {
632
- config.extends = [config.extends, earlyAnswers.styleguide];
633
- } else {
634
- config.extends = [earlyAnswers.styleguide];
635
- }
636
-
637
- const modules = getModulesList(config);
638
-
639
- return askInstallModules(modules, earlyAnswers.packageJsonExists)
640
- .then(() => writeFile(config, earlyAnswers.format));
641
-
642
- }
643
-
644
- if (earlyAnswers.source === "auto") {
645
- const combinedAnswers = Object.assign({}, earlyAnswers);
646
- const config = processAnswers(combinedAnswers);
647
- const modules = getModulesList(config);
648
-
649
- return askInstallModules(modules).then(() => writeFile(config, earlyAnswers.format));
650
- }
651
-
652
- // continue with the style questions otherwise...
653
- return enquirer.prompt([
654
- {
655
- type: "select",
656
- name: "indent",
657
- message: "What style of indentation do you use?",
658
- initial: 0,
659
- choices: [{ message: "Tabs", name: "tab" }, { message: "Spaces", name: 4 }]
660
- },
661
- {
662
- type: "select",
663
- name: "quotes",
664
- message: "What quotes do you use for strings?",
665
- initial: 0,
666
- choices: [{ message: "Double", name: "double" }, { message: "Single", name: "single" }]
667
- },
668
- {
669
- type: "select",
670
- name: "linebreak",
671
- message: "What line endings do you use?",
672
- initial: 0,
673
- choices: [{ message: "Unix", name: "unix" }, { message: "Windows", name: "windows" }]
674
- },
675
- {
676
- type: "toggle",
677
- name: "semi",
678
- message: "Do you require semicolons?",
679
- enabled: "Yes",
680
- disabled: "No",
681
- initial: 1
682
- }
683
- ]).then(answers => {
684
- const totalAnswers = Object.assign({}, earlyAnswers, answers);
685
-
686
- const config = processAnswers(totalAnswers);
687
- const modules = getModulesList(config);
688
-
689
- return askInstallModules(modules).then(() => writeFile(config, earlyAnswers.format));
690
- });
691
- });
692
- }
693
-
694
- //------------------------------------------------------------------------------
695
- // Public Interface
696
- //------------------------------------------------------------------------------
697
-
698
- const init = {
699
- getModulesList,
700
- hasESLintVersionConflict,
701
- installModules,
702
- processAnswers,
703
- writeFile,
704
- /* istanbul ignore next */initializeConfig() {
705
- return promptUser();
706
- }
707
- };
708
-
709
- module.exports = init;