-
-
Notifications
You must be signed in to change notification settings - Fork 9.5k
/
Copy patheslintPlugin.ts
121 lines (101 loc) · 4.03 KB
/
eslintPlugin.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import { existsSync } from 'node:fs';
import { readFile, writeFile } from 'node:fs/promises';
import type { JsPackageManager } from '@storybook/core/common';
import { paddedLog } from '@storybook/core/common';
import { readConfig, writeConfig } from '@storybook/core/csf-tools';
import chalk from 'chalk';
import detectIndent from 'detect-indent';
import prompts from 'prompts';
import { dedent } from 'ts-dedent';
export const SUPPORTED_ESLINT_EXTENSIONS = ['js', 'cjs', 'json'];
const UNSUPPORTED_ESLINT_EXTENSIONS = ['yaml', 'yml'];
export const findEslintFile = () => {
const filePrefix = '.eslintrc';
const unsupportedExtension = UNSUPPORTED_ESLINT_EXTENSIONS.find((ext: string) =>
existsSync(`${filePrefix}.${ext}`)
);
if (unsupportedExtension) {
throw new Error(unsupportedExtension);
}
const extension = SUPPORTED_ESLINT_EXTENSIONS.find((ext: string) =>
existsSync(`${filePrefix}.${ext}`)
);
return extension ? `${filePrefix}.${extension}` : null;
};
export async function extractEslintInfo(packageManager: JsPackageManager): Promise<{
hasEslint: boolean;
isStorybookPluginInstalled: boolean;
eslintConfigFile: string | null;
}> {
const allDependencies = await packageManager.getAllDependencies();
const packageJson = await packageManager.retrievePackageJson();
let eslintConfigFile: string | null = null;
try {
eslintConfigFile = findEslintFile();
} catch (err) {
//
}
const isStorybookPluginInstalled = !!allDependencies['eslint-plugin-storybook'];
const hasEslint = allDependencies.eslint || eslintConfigFile || packageJson.eslintConfig;
return { hasEslint, isStorybookPluginInstalled, eslintConfigFile };
}
export const normalizeExtends = (existingExtends: any): string[] => {
if (!existingExtends) {
return [];
}
if (typeof existingExtends === 'string') {
return [existingExtends];
}
if (Array.isArray(existingExtends)) {
return existingExtends;
}
throw new Error(`Invalid eslint extends ${existingExtends}`);
};
export async function configureEslintPlugin(
eslintFile: string | undefined,
packageManager: JsPackageManager
) {
if (eslintFile) {
paddedLog(`Configuring Storybook ESLint plugin at ${eslintFile}`);
if (eslintFile.endsWith('json')) {
const eslintConfig = JSON.parse(await readFile(eslintFile, { encoding: 'utf8' })) as {
extends?: string[];
};
const existingExtends = normalizeExtends(eslintConfig.extends).filter(Boolean);
eslintConfig.extends = [...existingExtends, 'plugin:storybook/recommended'] as string[];
const eslintFileContents = await readFile(eslintFile, { encoding: 'utf8' });
const spaces = detectIndent(eslintFileContents).amount || 2;
await writeFile(eslintFile, JSON.stringify(eslintConfig, undefined, spaces));
} else {
const eslint = await readConfig(eslintFile);
const existingExtends = normalizeExtends(eslint.getFieldValue(['extends'])).filter(Boolean);
eslint.setFieldValue(['extends'], [...existingExtends, 'plugin:storybook/recommended']);
await writeConfig(eslint);
}
} else {
paddedLog(`Configuring eslint-plugin-storybook in your package.json`);
const packageJson = await packageManager.retrievePackageJson();
const existingExtends = normalizeExtends(packageJson.eslintConfig?.extends).filter(Boolean);
await packageManager.writePackageJson({
...packageJson,
eslintConfig: {
...packageJson.eslintConfig,
extends: [...existingExtends, 'plugin:storybook/recommended'],
},
});
}
}
export const suggestESLintPlugin = async (): Promise<boolean> => {
const { shouldInstall } = await prompts({
type: 'confirm',
name: 'shouldInstall',
message: dedent`
We have detected that you're using ESLint. Storybook provides a plugin that gives the best experience with Storybook and helps follow best practices: ${chalk.yellow(
'https://github.com/storybookjs/eslint-plugin-storybook#readme'
)}
Would you like to install it?
`,
initial: true,
});
return shouldInstall;
};