Skip to content

Commit

Permalink
feat!: support flat config
Browse files Browse the repository at this point in the history
  • Loading branch information
aladdin-add committed Jan 23, 2024
1 parent f893814 commit 4c96a45
Show file tree
Hide file tree
Showing 32 changed files with 532 additions and 1,483 deletions.
8 changes: 8 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
root = true

[*]
indent_style = space
indent_size = 4
trim_trailing_whitespace = true
end_of_line = lf
insert_final_newline = true
14 changes: 9 additions & 5 deletions bin/create-config.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
#!/usr/bin/env node

/**
* @fileoverview Main CLI that is run via the eslint command.
* @author Nicholas C. Zakas
* @fileoverview Main CLI that is run via the `npm init @eslint/config` command.
* @author 唯然<[email protected]>
*/

/* eslint no-console:off -- CLI */
import { initializeConfig } from "../lib/init/config-initializer.js";
initializeConfig();
import { ConfigGenerator } from "../lib/config-generator.js";

const generator = new ConfigGenerator();

generator.prompt();
generator.calc();
generator.output();
146 changes: 146 additions & 0 deletions lib/config-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* @fileoverview to generate config files.
* @author 唯然<[email protected]>
*/
import process from "process";
import path from "path";
import { spawnSync } from "child_process";
import { writeFile } from "fs/promises";
import enquirer from "enquirer";
import { isPackageTypeModule, installSyncSaveDev } from "./utils/npm-utils.js";
import * as log from "./utils/logging.js";

/**
* Class representing a ConfigGenerator.
*/
export class ConfigGenerator {

/**
* Create a ConfigGenerator.
* @param {Object} options The options for the ConfigGenerator.
* @param {string} options.cwd The current working directory.
* @param {Object} options.answers The answers provided by the user.
* @returns {ConfigGenerator} The ConfigGenerator instance.
*/
constructor(options = {}) {
this.cwd = options.cwd || process.cwd();
this.answers = options.answers || {};
this.result = {
devDependencies: ["eslint"],
configFilename: "eslint.config.js",
configContent: ""
};
}

/**
* Prompt the user for input.
* @returns {void}
*/
prompt() {

// TODO: ask users to input
this.answers = {
purpose: "syntax",
module: "esm",
framework: "vue",
lang: "js",
env: ["browser", "node"]
};
}

// eslint-disable-next-line jsdoc/require-throws -- ts is not supported yet
/**
* Calculate the configuration based on the user's answers.
* @returns {void}
*/
calc() {
const isModule = isPackageTypeModule(this.cwd);

this.result.configFilename = isModule ? "eslint.config.js" : "eslint.config.mjs";

let importContent = "";
let exportContent = "";
let languageOptionsContent = "";

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

// no need to install any plugin
} else if (this.answers.purpose === "problem") {
this.result.devDependencies.push("@eslint/js");
importContent += "import pluginJs from \"@eslint/js\";\n";
exportContent += " pluginJs.configs.recommended,\n";
} else if (this.answers.purpose === "style") {

// TODO: style
}

if (this.answers.module === "commonjs") {
languageOptionsContent += "sourceType: \"commonjs\", ";
}
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}"
};

languageOptionsContent += `${envContent[this.answers.env.join(",")]}, `;
}


if (this.answers.lang === "ts") {
throw new Error("typescript is not supported yet.");

// this.result.devDependencies.push("@typescript-eslint/eslint-plugin");
// importContent += "import pluginTs from \"@typescript-eslint/eslint-plugin\";\n";
// exportContent += " pluginTs.configs.recommended,\n";
}

if (this.answers.framework === "vue") {
this.result.devDependencies.push("eslint-plugin-vue");
importContent += "import pluginVue from \"eslint-plugin-vue\";\n";
exportContent += " pluginVue.configs.recommended,\n";
}

if (this.answers.framework === "react") {
this.result.devDependencies.push("eslint-plugin-react");
importContent += "import pluginReactConfig from \"eslint-plugin-react/configs/recommended.js\";\n";
exportContent += " pluginReactConfig,\n";
}

this.result.configContent = `${importContent}export default [\n { ${languageOptionsContent}},\n${exportContent}];`;
}

/**
* Output the configuration.
* @returns {void}
*/
async output() {

// TODO: is peerDependencies still needed?
const packageManager = (await enquirer.prompt({
type: "select",
name: "packageManager",
message: "Which package manager do you want to use?",
initial: 0,
choices: ["npm", "yarn", "pnpm", "bun"]
})).packageManager;

installSyncSaveDev(this.result.devDependencies, packageManager);

const configPath = path.join(this.cwd, this.result.configFilename);

await writeFile(configPath, this.result.configContent);

// import("eslint") won't work in some cases.
// refs: https://github.com/eslint/create-config/issues/8, https://github.com/eslint/create-config/issues/12
const eslintBin = path.join(this.cwd, "./node_modules/.bin/eslint");
const result = spawnSync(eslintBin, ["--fix", "--quiet", configPath], { encoding: "utf8" });

if (result.error || result.status !== 0) {
log.error("A config file was generated, but the config file itself may not follow your linting rules.");
}
}
}
134 changes: 0 additions & 134 deletions lib/init/config-file.js

This file was deleted.

Loading

0 comments on commit 4c96a45

Please sign in to comment.