Skip to content

Commit ba12b34

Browse files
committed
feat: support languages json/markdown
fixes #148 resubmit #151
1 parent c3f9ffd commit ba12b34

32 files changed

+471
-157
lines changed

lib/config-generator.js

Lines changed: 88 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import enquirer from "enquirer";
1515
import { isPackageTypeModule, installSyncSaveDev, fetchPeerDependencies, findPackageJson } from "./utils/npm-utils.js";
1616
import { getShorthandName } from "./utils/naming.js";
1717
import * as log from "./utils/logging.js";
18+
import { langQuestions, jsQuestions, mdQuestions, installationQuestions } from "./questions.js";
1819

1920
//-----------------------------------------------------------------------------
2021
// Helpers
@@ -47,6 +48,17 @@ function getExtensions(answers) {
4748
return extensions;
4849
}
4950

51+
const helperContent = `import path from "node:path";
52+
import { fileURLToPath } from "node:url";
53+
import { FlatCompat } from "@eslint/eslintrc";
54+
import js from "@eslint/js";
55+
56+
// mimic CommonJS variables -- not needed if using CommonJS
57+
const __filename = fileURLToPath(import.meta.url);
58+
const __dirname = path.dirname(__filename);
59+
const compat = new FlatCompat({baseDirectory: __dirname, recommendedConfig: js.configs.recommended});
60+
`;
61+
5062
//-----------------------------------------------------------------------------
5163
// Exports
5264
//-----------------------------------------------------------------------------
@@ -80,65 +92,15 @@ export class ConfigGenerator {
8092
* @returns {void}
8193
*/
8294
async prompt() {
83-
const questions = [
84-
{
85-
type: "select",
86-
name: "purpose",
87-
message: "How would you like to use ESLint?",
88-
initial: 1,
89-
choices: [
90-
{ message: "To check syntax only", name: "syntax" },
91-
{ message: "To check syntax and find problems", name: "problems" }
92-
]
93-
},
94-
{
95-
type: "select",
96-
name: "moduleType",
97-
message: "What type of modules does your project use?",
98-
initial: 0,
99-
choices: [
100-
{ message: "JavaScript modules (import/export)", name: "esm" },
101-
{ message: "CommonJS (require/exports)", name: "commonjs" },
102-
{ message: "None of these", name: "script" }
103-
]
104-
},
105-
{
106-
type: "select",
107-
name: "framework",
108-
message: "Which framework does your project use?",
109-
initial: 0,
110-
choices: [
111-
{ message: "React", name: "react" },
112-
{ message: "Vue.js", name: "vue" },
113-
{ message: "None of these", name: "none" }
114-
]
115-
},
116-
{
117-
type: "select",
118-
name: "language",
119-
message: "Does your project use TypeScript?",
120-
choices: [
121-
{ message: "No", name: "javascript" },
122-
{ message: "Yes", name: "typescript" }
123-
],
124-
initial: 0
125-
},
126-
{
127-
type: "multiselect",
128-
name: "env",
129-
message: "Where does your code run?",
130-
hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
131-
initial: 0,
132-
choices: [
133-
{ message: "Browser", name: "browser" },
134-
{ message: "Node", name: "node" }
135-
]
136-
}
137-
];
95+
Object.assign(this.answers, await enquirer.prompt(langQuestions));
13896

139-
const answers = await enquirer.prompt(questions);
97+
if (this.answers.languages.includes("javascript")) {
98+
Object.assign(this.answers, await enquirer.prompt(jsQuestions));
99+
}
140100

141-
Object.assign(this.answers, answers);
101+
if (this.answers.languages.includes("md")) {
102+
Object.assign(this.answers, await enquirer.prompt(mdQuestions));
103+
}
142104
}
143105

144106
/**
@@ -155,45 +117,40 @@ export class ConfigGenerator {
155117
const extensions = `**/*.{${getExtensions(this.answers)}}`;
156118

157119
let importContent = "import { defineConfig } from \"eslint/config\";\n";
158-
const helperContent = `import path from "node:path";
159-
import { fileURLToPath } from "node:url";
160-
import { FlatCompat } from "@eslint/eslintrc";
161-
import js from "@eslint/js";
162-
163-
// mimic CommonJS variables -- not needed if using CommonJS
164-
const __filename = fileURLToPath(import.meta.url);
165-
const __dirname = path.dirname(__filename);
166-
const compat = new FlatCompat({baseDirectory: __dirname, recommendedConfig: js.configs.recommended});
167-
`;
168120
let exportContent = "";
169121
let needCompatHelper = false;
122+
const languages = this.answers.languages ?? ["javascript"];
123+
const purpose = this.answers.purpose;
124+
const useTs = this.answers.useTs;
170125

171-
if (this.answers.moduleType === "commonjs" || this.answers.moduleType === "script") {
172-
exportContent += ` { files: ["**/*.js"], languageOptions: { sourceType: "${this.answers.moduleType}" } },\n`;
173-
}
126+
if (languages.includes("javascript")) {
127+
if (this.answers.moduleType === "commonjs" || this.answers.moduleType === "script") {
128+
exportContent += ` { files: ["**/*.js"], languageOptions: { sourceType: "${this.answers.moduleType}" } },\n`;
129+
}
174130

175-
if (this.answers.env?.length > 0) {
176-
this.result.devDependencies.push("globals");
177-
importContent += "import globals from \"globals\";\n";
178-
const envContent = {
179-
browser: "globals: globals.browser",
180-
node: "globals: globals.node",
181-
"browser,node": "globals: {...globals.browser, ...globals.node}"
182-
};
131+
if (this.answers.env?.length > 0) {
132+
this.result.devDependencies.push("globals");
133+
importContent += "import globals from \"globals\";\n";
134+
const envContent = {
135+
browser: "globals: globals.browser",
136+
node: "globals: globals.node",
137+
"browser,node": "globals: {...globals.browser, ...globals.node}"
138+
};
183139

184-
exportContent += ` { files: ["${extensions}"], languageOptions: { ${envContent[this.answers.env.join(",")]} } },\n`;
140+
exportContent += ` { files: ["${extensions}"], languageOptions: { ${envContent[this.answers.env.join(",")]} } },\n`;
141+
}
185142
}
186143

187-
if (this.answers.purpose === "syntax") {
144+
if (purpose === "syntax") {
188145

189146
// no need to install any plugin
190-
} else if (this.answers.purpose === "problems") {
147+
} else if (purpose === "problems") {
191148
this.result.devDependencies.push("@eslint/js");
192149
importContent += "import js from \"@eslint/js\";\n";
193150
exportContent += ` { files: ["${extensions}"], plugins: { js }, extends: ["js/recommended"] },\n`;
194151
}
195152

196-
if (this.answers.language === "typescript") {
153+
if (useTs) {
197154
this.result.devDependencies.push("typescript-eslint");
198155
importContent += "import tseslint from \"typescript-eslint\";\n";
199156
exportContent += " tseslint.configs.recommended,\n";
@@ -216,6 +173,53 @@ const compat = new FlatCompat({baseDirectory: __dirname, recommendedConfig: js.c
216173
exportContent += " pluginReact.configs.flat.recommended,\n";
217174
}
218175

176+
if (languages.some(item => item.startsWith("json"))) {
177+
this.result.devDependencies.push("@eslint/json");
178+
importContent += "import json from \"@eslint/json\";\n";
179+
}
180+
if (languages.includes("json")) {
181+
const config = purpose === "syntax"
182+
? " {files: [\"**/*.json\"], plugins: {json}, language: \"json/json\"},\n"
183+
: " {files: [\"**/*.json\"], language: \"json/json\", ...json.configs.recommended},\n";
184+
185+
exportContent += config;
186+
}
187+
if (languages.includes("jsonc")) {
188+
const config = purpose === "syntax"
189+
? " {files: [\"**/*.jsonc\"], plugins: {json}, language: \"json/jsonc\"},\n"
190+
: " {files: [\"**/*.jsonc\"], language: \"json/jsonc\", ...json.configs.recommended},\n";
191+
192+
exportContent += config;
193+
}
194+
if (languages.includes("json5")) {
195+
const config = purpose === "syntax"
196+
? " {files: [\"**/*.json5\"], plugins: {json}, language: \"json/json5\"},\n"
197+
: " {files: [\"**/*.json5\"], language: \"json/json5\", ...json.configs.recommended},\n";
198+
199+
exportContent += config;
200+
}
201+
202+
if (languages.includes("md")) {
203+
this.result.devDependencies.push("@eslint/markdown");
204+
importContent += "import markdown from \"@eslint/markdown\";\n";
205+
206+
if (purpose === "syntax") {
207+
const config = this.answers.mdType === "commonmark"
208+
? " {files: [\"**/*.md\"], plugins: {markdown}, language: \"markdown/commonmark\"},\n"
209+
: " {files: [\"**/*.md\"], plugins: {markdown}, language: \"markdown/gfm\"},\n";
210+
211+
exportContent += config;
212+
} else if (purpose === "problems") {
213+
exportContent += " ...markdown.configs.recommended,\n";
214+
215+
if (this.answers.mdType === "gfm") {
216+
217+
// the default is commonmark
218+
exportContent += " {files: [\"**/*.md\"], language: \"markdown/gfm\"},\n";
219+
}
220+
}
221+
}
222+
219223
if (this.answers.config) {
220224
const config = this.answers.config;
221225

@@ -274,24 +278,8 @@ export default defineConfig([\n${exportContent || " {}\n"}]);`; // defaults to
274278
log.info("The config that you've selected requires the following dependencies:\n");
275279
log.info(this.result.devDependencies.join(", "));
276280

277-
const questions = [{
278-
type: "toggle",
279-
name: "executeInstallation",
280-
message: "Would you like to install them now?",
281-
enabled: "Yes",
282-
disabled: "No",
283-
initial: 1
284-
}, {
285-
type: "select",
286-
name: "packageManager",
287-
message: "Which package manager do you want to use?",
288-
initial: 0,
289-
choices: ["npm", "yarn", "pnpm", "bun"],
290-
skip() {
291-
return this.state.answers.executeInstallation === false;
292-
}
293-
}];
294-
const { executeInstallation, packageManager } = (await enquirer.prompt(questions));
281+
282+
const { executeInstallation, packageManager } = (await enquirer.prompt(installationQuestions));
295283
const configPath = path.join(this.cwd, this.result.configFilename);
296284

297285
if (executeInstallation === true) {

lib/questions.js

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* @fileoverview all the questions for the quiz
3+
* @author 唯然<[email protected]>
4+
*/
5+
6+
export const langQuestions = [{
7+
type: "multiselect",
8+
name: "languages",
9+
message: "What do you want to lint?",
10+
choices: [
11+
{ message: "JavaScript", name: "javascript" },
12+
{ message: "JSON", name: "json" },
13+
{ message: "JSON with comments", name: "jsonc" },
14+
{ message: "JSON5", name: "json5" },
15+
{ message: "Markdown", name: "md" }
16+
],
17+
initial: 0
18+
}, {
19+
type: "select",
20+
name: "purpose",
21+
message: "How would you like to use ESLint?",
22+
initial: 1,
23+
choices: [
24+
{ message: "To check syntax only", name: "syntax" },
25+
{ message: "To check syntax and find problems", name: "problems" }
26+
]
27+
}];
28+
29+
export const jsQuestions = [
30+
{
31+
type: "select",
32+
name: "moduleType",
33+
message: "What type of modules does your project use?",
34+
initial: 0,
35+
choices: [
36+
{ message: "JavaScript modules (import/export)", name: "esm" },
37+
{ message: "CommonJS (require/exports)", name: "commonjs" },
38+
{ message: "None of these", name: "script" }
39+
]
40+
},
41+
{
42+
type: "select",
43+
name: "framework",
44+
message: "Which framework does your project use?",
45+
initial: 0,
46+
choices: [
47+
{ message: "React", name: "react" },
48+
{ message: "Vue.js", name: "vue" },
49+
{ message: "None of these", name: "none" }
50+
]
51+
},
52+
{
53+
type: "toggle",
54+
name: "useTs",
55+
message: "Does your project use TypeScript?",
56+
initial: 0
57+
},
58+
{
59+
type: "multiselect",
60+
name: "env",
61+
message: "Where does your code run?",
62+
hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
63+
initial: 0,
64+
choices: [
65+
{ message: "Browser", name: "browser" },
66+
{ message: "Node", name: "node" }
67+
]
68+
}
69+
];
70+
71+
export const mdQuestions = [{
72+
type: "select",
73+
name: "mdType",
74+
message: "What flavor of Markdown do you want to lint?",
75+
initial: 0,
76+
choices: [
77+
{ message: "CommonMark", name: "commonmark" },
78+
{ message: "GitHub Flavored Markdown", name: "gfm" }
79+
]
80+
}];
81+
82+
export const installationQuestions = [
83+
{
84+
type: "toggle",
85+
name: "executeInstallation",
86+
message: "Would you like to install them now?",
87+
enabled: "Yes",
88+
disabled: "No",
89+
initial: 1
90+
}, {
91+
type: "select",
92+
name: "packageManager",
93+
message: "Which package manager do you want to use?",
94+
initial: 0,
95+
choices: ["npm", "yarn", "pnpm", "bun"],
96+
skip() {
97+
return this.state.answers.executeInstallation === false;
98+
}
99+
}
100+
];
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"configContent": "import { defineConfig } from "eslint/config";
3+
import globals from "globals";
4+
import js from "@eslint/js";
5+
import json from "@eslint/json";
6+
7+
8+
export default defineConfig([
9+
{ files: ["**/*.{js,mjs,cjs}"] },
10+
{ files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: globals.node } },
11+
{ files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"] },
12+
{files: ["**/*.json"], language: "json/json", ...json.configs.recommended},
13+
]);",
14+
"configFilename": "eslint.config.js",
15+
"devDependencies": [
16+
"eslint",
17+
"globals",
18+
"@eslint/js",
19+
"@eslint/json",
20+
],
21+
"installFlags": [
22+
"-D",
23+
],
24+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"configContent": "import { defineConfig } from "eslint/config";
3+
import js from "@eslint/js";
4+
import json from "@eslint/json";
5+
6+
7+
export default defineConfig([
8+
{ files: ["**/*.{js,mjs,cjs}"] },
9+
{ files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"] },
10+
{files: ["**/*.json"], language: "json/json", ...json.configs.recommended},
11+
]);",
12+
"configFilename": "eslint.config.js",
13+
"devDependencies": [
14+
"eslint",
15+
"@eslint/js",
16+
"@eslint/json",
17+
],
18+
"installFlags": [
19+
"-D",
20+
],
21+
}

0 commit comments

Comments
 (0)