Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
192 changes: 90 additions & 102 deletions lib/config-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import enquirer from "enquirer";
import { isPackageTypeModule, installSyncSaveDev, fetchPeerDependencies, findPackageJson } from "./utils/npm-utils.js";
import { getShorthandName } from "./utils/naming.js";
import * as log from "./utils/logging.js";
import { langQuestions, jsQuestions, mdQuestions, installationQuestions } from "./questions.js";

//-----------------------------------------------------------------------------
// Helpers
Expand All @@ -28,7 +29,7 @@ import * as log from "./utils/logging.js";
function getExtensions(answers) {
const extensions = ["js", "mjs", "cjs"];

if (answers.language === "typescript") {
if (answers.useTs) {
extensions.push("ts");
}

Expand All @@ -39,14 +40,25 @@ function getExtensions(answers) {
if (answers.framework === "react") {
extensions.push("jsx");

if (answers.language === "typescript") {
if (answers.useTs) {
extensions.push("tsx");
}
}

return extensions;
}

const helperContent = `import path from "node:path";
import { fileURLToPath } from "node:url";
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({baseDirectory: __dirname, recommendedConfig: js.configs.recommended});
`;

//-----------------------------------------------------------------------------
// Exports
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -80,65 +92,15 @@ export class ConfigGenerator {
* @returns {void}
*/
async prompt() {
const questions = [
{
type: "select",
name: "purpose",
message: "How would you like to use ESLint?",
initial: 1,
choices: [
{ message: "To check syntax only", name: "syntax" },
{ message: "To check syntax and find problems", name: "problems" }
]
},
{
type: "select",
name: "moduleType",
message: "What type of modules does your project use?",
initial: 0,
choices: [
{ message: "JavaScript modules (import/export)", name: "esm" },
{ message: "CommonJS (require/exports)", name: "commonjs" },
{ message: "None of these", name: "script" }
]
},
{
type: "select",
name: "framework",
message: "Which framework does your project use?",
initial: 0,
choices: [
{ message: "React", name: "react" },
{ message: "Vue.js", name: "vue" },
{ message: "None of these", name: "none" }
]
},
{
type: "select",
name: "language",
message: "Does your project use TypeScript?",
choices: [
{ message: "No", name: "javascript" },
{ message: "Yes", name: "typescript" }
],
initial: 0
},
{
type: "multiselect",
name: "env",
message: "Where does your code run?",
hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
initial: 0,
choices: [
{ message: "Browser", name: "browser" },
{ message: "Node", name: "node" }
]
}
];
Object.assign(this.answers, await enquirer.prompt(langQuestions));

const answers = await enquirer.prompt(questions);
if (this.answers.languages.includes("javascript")) {
Object.assign(this.answers, await enquirer.prompt(jsQuestions));
}

Object.assign(this.answers, answers);
if (this.answers.languages.includes("md")) {
Object.assign(this.answers, await enquirer.prompt(mdQuestions));
}
}

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

let importContent = "import { defineConfig } from \"eslint/config\";\n";
const helperContent = `import path from "node:path";
import { fileURLToPath } from "node:url";
import { FlatCompat } from "@eslint/eslintrc";
import js from "@eslint/js";

// mimic CommonJS variables -- not needed if using CommonJS
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({baseDirectory: __dirname, recommendedConfig: js.configs.recommended});
`;
let exportContent = "";
let needCompatHelper = false;
const languages = this.answers.languages ?? ["javascript"];
const purpose = this.answers.purpose;
const useTs = this.answers.useTs;

if (this.answers.moduleType === "commonjs" || this.answers.moduleType === "script") {
exportContent += ` { files: ["**/*.js"], languageOptions: { sourceType: "${this.answers.moduleType}" } },\n`;
}
if (languages.includes("javascript")) {
if (this.answers.moduleType === "commonjs" || this.answers.moduleType === "script") {
exportContent += ` { files: ["**/*.js"], languageOptions: { sourceType: "${this.answers.moduleType}" } },\n`;
}

if (this.answers.env?.length > 0) {
this.result.devDependencies.push("globals");
importContent += "import globals from \"globals\";\n";
const envContent = {
browser: "globals: globals.browser",
node: "globals: globals.node",
"browser,node": "globals: {...globals.browser, ...globals.node}"
};
if (this.answers.env?.length > 0) {
this.result.devDependencies.push("globals");
importContent += "import globals from \"globals\";\n";
const envContent = {
browser: "globals: globals.browser",
node: "globals: globals.node",
"browser,node": "globals: {...globals.browser, ...globals.node}"
};

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

if (this.answers.purpose === "syntax") {
if (purpose === "syntax") {

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

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

if (languages.some(item => item.startsWith("json"))) {
this.result.devDependencies.push("@eslint/json");
importContent += "import json from \"@eslint/json\";\n";
}
if (languages.includes("json")) {
const config = purpose === "syntax"
? " {files: [\"**/*.json\"], plugins: {json}, language: \"json/json\"},\n"
: " {files: [\"**/*.json\"], language: \"json/json\", ...json.configs.recommended},\n";

exportContent += config;
}
if (languages.includes("jsonc")) {
const config = purpose === "syntax"
? " {files: [\"**/*.jsonc\"], plugins: {json}, language: \"json/jsonc\"},\n"
: " {files: [\"**/*.jsonc\"], language: \"json/jsonc\", ...json.configs.recommended},\n";

exportContent += config;
}
if (languages.includes("json5")) {
const config = purpose === "syntax"
? " {files: [\"**/*.json5\"], plugins: {json}, language: \"json/json5\"},\n"
: " {files: [\"**/*.json5\"], language: \"json/json5\", ...json.configs.recommended},\n";

exportContent += config;
}

if (languages.includes("md")) {
this.result.devDependencies.push("@eslint/markdown");
importContent += "import markdown from \"@eslint/markdown\";\n";

if (purpose === "syntax") {
const config = this.answers.mdType === "commonmark"
? " {files: [\"**/*.md\"], plugins: {markdown}, language: \"markdown/commonmark\"},\n"
: " {files: [\"**/*.md\"], plugins: {markdown}, language: \"markdown/gfm\"},\n";

exportContent += config;
} else if (purpose === "problems") {
exportContent += " ...markdown.configs.recommended,\n";

if (this.answers.mdType === "gfm") {

// the default is commonmark
exportContent += " {files: [\"**/*.md\"], language: \"markdown/gfm\"},\n";
}
}
}

if (this.answers.config) {
const config = this.answers.config;

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

const questions = [{
type: "toggle",
name: "executeInstallation",
message: "Would you like to install them now?",
enabled: "Yes",
disabled: "No",
initial: 1
}, {
type: "select",
name: "packageManager",
message: "Which package manager do you want to use?",
initial: 0,
choices: ["npm", "yarn", "pnpm", "bun"],
skip() {
return this.state.answers.executeInstallation === false;
}
}];
const { executeInstallation, packageManager } = (await enquirer.prompt(questions));

const { executeInstallation, packageManager } = (await enquirer.prompt(installationQuestions));
const configPath = path.join(this.cwd, this.result.configFilename);

if (executeInstallation === true) {
Expand Down
100 changes: 100 additions & 0 deletions lib/questions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @fileoverview all the questions for the quiz
* @author 唯然<[email protected]>
*/

export const langQuestions = [{
type: "multiselect",
name: "languages",
message: "What do you want to lint?",
choices: [
{ message: "JavaScript", name: "javascript" },
{ message: "JSON", name: "json" },
{ message: "JSON with comments", name: "jsonc" },
{ message: "JSON5", name: "json5" },
{ message: "Markdown", name: "md" }
],
initial: 0
}, {
type: "select",
name: "purpose",
message: "How would you like to use ESLint?",
initial: 1,
choices: [
{ message: "To check syntax only", name: "syntax" },
{ message: "To check syntax and find problems", name: "problems" }
]
}];

export const jsQuestions = [
{
type: "select",
name: "moduleType",
message: "What type of modules does your project use?",
initial: 0,
choices: [
{ message: "JavaScript modules (import/export)", name: "esm" },
{ message: "CommonJS (require/exports)", name: "commonjs" },
{ message: "None of these", name: "script" }
]
},
{
type: "select",
name: "framework",
message: "Which framework does your project use?",
initial: 0,
choices: [
{ message: "React", name: "react" },
{ message: "Vue.js", name: "vue" },
{ message: "None of these", name: "none" }
]
},
{
type: "toggle",
name: "useTs",
message: "Does your project use TypeScript?",
initial: 0
},
{
type: "multiselect",
name: "env",
message: "Where does your code run?",
hint: "(Press <space> to select, <a> to toggle all, <i> to invert selection)",
initial: 0,
choices: [
{ message: "Browser", name: "browser" },
{ message: "Node", name: "node" }
]
}
];

export const mdQuestions = [{
type: "select",
name: "mdType",
message: "What flavor of Markdown do you want to lint?",
initial: 0,
choices: [
{ message: "CommonMark", name: "commonmark" },
{ message: "GitHub Flavored Markdown", name: "gfm" }
]
}];

export const installationQuestions = [
{
type: "toggle",
name: "executeInstallation",
message: "Would you like to install them now?",
enabled: "Yes",
disabled: "No",
initial: 1
}, {
type: "select",
name: "packageManager",
message: "Which package manager do you want to use?",
initial: 0,
choices: ["npm", "yarn", "pnpm", "bun"],
skip() {
return this.state.answers.executeInstallation === false;
}
}
];
24 changes: 24 additions & 0 deletions tests/__snapshots__/esm-javascript-json-problems
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"configContent": "import { defineConfig } from "eslint/config";
import globals from "globals";
import js from "@eslint/js";
import json from "@eslint/json";


export default defineConfig([
{ files: ["**/*.{js,mjs,cjs}"] },
{ files: ["**/*.{js,mjs,cjs}"], languageOptions: { globals: globals.node } },
{ files: ["**/*.{js,mjs,cjs}"], plugins: { js }, extends: ["js/recommended"] },
{files: ["**/*.json"], language: "json/json", ...json.configs.recommended},
]);",
"configFilename": "eslint.config.js",
"devDependencies": [
"eslint",
"globals",
"@eslint/js",
"@eslint/json",
],
"installFlags": [
"-D",
],
}
Loading