Skip to content

Commit

Permalink
Chore: restructure files (#11555)
Browse files Browse the repository at this point in the history
  • Loading branch information
mysticatea committed May 22, 2019
1 parent 5dad0b1 commit 219aecb
Show file tree
Hide file tree
Showing 564 changed files with 1,264 additions and 1,022 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Expand Up @@ -7,6 +7,6 @@
/tests/fixtures/**
/tests/performance/**
/tmp/**
/lib/util/unicode/is-combining-character.js
/lib/rules/utils/unicode/is-combining-character.js
test.js
!.eslintrc.js
130 changes: 122 additions & 8 deletions .eslintrc.js
@@ -1,5 +1,14 @@
"use strict";

const internalFiles = [
"**/cli-engine/**/*",
"**/init/**/*",
"**/linter/**/*",
"**/rule-tester/**/*",
"**/rules/**/*",
"**/source-code/**/*"
];

module.exports = {
root: true,
plugins: [
Expand Down Expand Up @@ -39,7 +48,7 @@ module.exports = {
overrides: [
{
files: ["lib/rules/*", "tools/internal-rules/*"],
excludedFiles: ["tools/internal-rules/index.js"],
excludedFiles: ["index.js"],
rules: {
"internal-rules/no-invalid-meta": "error",
"internal-rules/consistent-docs-description": "error"
Expand All @@ -49,17 +58,15 @@ module.exports = {
* "internal-rules/consistent-meta-messages": "error"
*/
}
}, {
files: ["tools/internal-rules/*"],
rules: {
"node/no-unpublished-require": "off"
}
}, {
},
{
files: ["lib/rules/*"],
excludedFiles: ["index.js"],
rules: {
"internal-rules/consistent-docs-url": "error"
}
}, {
},
{
files: ["tests/**/*"],
env: { mocha: true },
rules: {
Expand All @@ -68,6 +75,113 @@ module.exports = {
message: "`assert.doesNotThrow()` should be replaced with a comment next to the code."
}]
}
},

// Restrict relative path imports
{
files: ["lib/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles
]
}]
}
},
{
files: ["lib/cli-engine/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"**/init"
]
}]
}
},
{
files: ["lib/init/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"**/rule-tester"
]
}]
}
},
{
files: ["lib/linter/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"fs",
"**/cli-engine",
"**/init",
"**/rule-tester"
]
}]
}
},
{
files: ["lib/rules/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"fs",
"**/cli-engine",
"**/init",
"**/linter",
"**/rule-tester",
"**/source-code"
]
}]
}
},
{
files: ["lib/shared/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"**/cli-engine",
"**/init",
"**/linter",
"**/rule-tester",
"**/source-code"
]
}]
}
},
{
files: ["lib/source-code/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"fs",
"**/cli-engine",
"**/init",
"**/linter",
"**/rule-tester",
"**/rules"
]
}]
}
},
{
files: ["lib/rule-tester/**/*"],
rules: {
"no-restricted-modules": ["error", {
patterns: [
...internalFiles,
"**/cli-engine",
"**/init"
]
}]
}
}
]
};
17 changes: 8 additions & 9 deletions Makefile.js
Expand Up @@ -64,6 +64,7 @@ const NODE = "node ", // intentional extra space
ESLINT = `${NODE} bin/eslint.js --report-unused-disable-directives `,

// Files
RULE_FILES = glob.sync("lib/rules/*.js").filter(filePath => path.basename(filePath) !== "index.js"),
JSON_FILES = find("conf/").filter(fileType("json")),
MARKDOWN_FILES_ARRAY = find("docs/").concat(ls(".")).filter(fileType("md")),
TEST_FILES = "\"tests/{bin,lib,tools}/**/*.js\"",
Expand Down Expand Up @@ -119,10 +120,10 @@ function execSilent(cmd) {
* @private
*/
function generateBlogPost(releaseInfo, prereleaseMajorVersion) {
const ruleList = ls("lib/rules")
const ruleList = RULE_FILES

// Strip the .js extension
.map(ruleFileName => ruleFileName.replace(/\.js$/u, ""))
.map(ruleFileName => path.basename(ruleFileName, ".js"))

/*
* Sort by length descending. This ensures that rule names which are substrings of other rule names are not
Expand Down Expand Up @@ -171,15 +172,14 @@ function generateFormatterExamples(formatterInfo, prereleaseVersion) {

/**
* Generate a doc page that lists all of the rules and links to them
* @param {string} basedir The directory in which to look for code.
* @returns {void}
*/
function generateRuleIndexPage(basedir) {
function generateRuleIndexPage() {
const outputFile = "../eslint.github.io/_data/rules.yml",
categoryList = "conf/category-list.json",
categoriesData = JSON.parse(cat(path.resolve(categoryList)));

find(path.join(basedir, "/lib/rules/")).filter(fileType("js"))
RULE_FILES
.map(filename => [filename, path.basename(filename, ".js")])
.sort((a, b) => a[1].localeCompare(b[1]))
.forEach(pair => {
Expand Down Expand Up @@ -792,10 +792,9 @@ target.checkRuleFiles = function() {
echo("Validating rules");

const ruleTypes = require("./tools/rule-types.json");
const ruleFiles = find("lib/rules/").filter(fileType("js"));
let errors = 0;

ruleFiles.forEach(filename => {
RULE_FILES.forEach(filename => {
const basename = path.basename(filename, ".js");
const docFilename = `docs/rules/${basename}.md`;

Expand Down Expand Up @@ -847,11 +846,11 @@ target.checkRuleFiles = function() {
}

// check parity between rules index file and rules directory
const ruleIdsInIndex = require("./lib/built-in-rules-index");
const ruleIdsInIndex = require("./lib/rules/index");
const ruleDef = ruleIdsInIndex.get(basename);

if (!ruleDef) {
console.error(`Missing rule from index (./lib/built-in-rules-index.js): ${basename}. If you just added a new rule then add an entry for it in this file.`);
console.error(`Missing rule from index (./lib/rules/index.js): ${basename}. If you just added a new rule then add an entry for it in this file.`);
errors++;
}

Expand Down
2 changes: 1 addition & 1 deletion bin/eslint.js
Expand Up @@ -65,7 +65,7 @@ if (useStdIn) {

process.exitCode = cli.execute(process.argv, fs.readFileSync(STDIN_FILE_DESCRIPTOR, "utf8"));
} else if (init) {
const configInit = require("../lib/config/config-initializer");
const configInit = require("../lib/init/config-initializer");

configInit.initializeConfig().then(() => {
process.exitCode = 0;
Expand Down
2 changes: 1 addition & 1 deletion conf/environments.js
Expand Up @@ -14,7 +14,7 @@ const globals = require("globals");
// Public Interface
//------------------------------------------------------------------------------

/** @type {Map<string, import("../lib/util/types").Environment>} */
/** @type {Map<string, import("../lib/shared/types").Environment>} */
module.exports = new Map(Object.entries({
builtin: {
globals: globals.es5
Expand Down
4 changes: 2 additions & 2 deletions conf/eslint-all.js
Expand Up @@ -9,7 +9,7 @@
// Requirements
//------------------------------------------------------------------------------

const builtInRules = require("../lib/built-in-rules-index");
const builtInRules = require("../lib/rules");

//------------------------------------------------------------------------------
// Helpers
Expand All @@ -27,5 +27,5 @@ for (const [ruleId, rule] of builtInRules) {
// Public Interface
//------------------------------------------------------------------------------

/** @type {import("../lib/util/types").ConfigData} */
/** @type {import("../lib/shared/types").ConfigData} */
module.exports = { rules: allRules };
2 changes: 1 addition & 1 deletion conf/eslint-recommended.js
Expand Up @@ -8,7 +8,7 @@

/* eslint sort-keys: ["error", "asc"] */

/** @type {import("../lib/util/types").ConfigData} */
/** @type {import("../lib/shared/types").ConfigData} */
module.exports = {
rules: {
"constructor-super": "error",
Expand Down
24 changes: 14 additions & 10 deletions docs/developer-guide/architecture.md
@@ -1,13 +1,18 @@
# Architecture

<center><img alt="dependency graph" src="./architecture/dependency.svg"></center>

At a high level, there are a few key parts to ESLint:

* `bin/eslint.js` - this is the file that actually gets executed with the command line utility. It's a dumb wrapper that does nothing more than bootstrap ESLint, passing the command line arguments to `cli`. This is intentionally small so as not to require heavy testing.
* `lib/api.js` - this is the entry point of `require("eslint")`. This file exposes an object that contains public classes `Linter`, `CLIEngine`, `RuleTester`, and `SourceCode`.
* `lib/cli.js` - this is the heart of the ESLint CLI. It takes an array of arguments and then uses `eslint` to execute the commands. By keeping this as a separate utility, it allows others to effectively call ESLint from within another Node.js program as if it were done on the command line. The main call is `cli.execute()`. This is also the part that does all the file reading, directory traversing, input, and output.
* `lib/linter.js` - this is the core Linter class that does code verifying based on configuration options. This file does no file I/O and does not interact with the `console` at all. For other Node.js programs that have JavaScript text to verify, they would be able to use this interface directly.
* `lib/api.js` - this exposes an object that contains Linter, CLIEngine, RuleTester, and SourceCode.
* `lib/testers/rule-tester.js` - this is a wrapper around Mocha, so that rules can be unit tested. This class lets us write consistently formatted tests for each rule that is implemented and be confident that each of the rules work. The RuleTester interface was modeled after Mocha and works with Mocha's global testing methods. RuleTester can also be modified to work with other testing frameworks.
* `lib/util/source-code.js` - this contains a SourceCode class that is used to represent the parsed source code. It takes in source code and the Program node of the AST representing the code.
* `lib/init/` - this module contains `--init` functionality that set up a configuration file for end users.
* `lib/cli-engine/` - this module is `CLIEngine` class that finds source code files and configuration files then does code verifying with the `Linter` class. This includes the loading logic of configuration files, parsers, plugins, and formatters.
* `lib/linter/` - this module is the core `Linter` class that does code verifying based on configuration options. This file does no file I/O and does not interact with the `console` at all. For other Node.js programs that have JavaScript text to verify, they would be able to use this interface directly.
* `lib/rule-tester/` - this module is `RuleTester` class that is a wrapper around Mocha so that rules can be unit tested. This class lets us write consistently formatted tests for each rule that is implemented and be confident that each of the rules work. The RuleTester interface was modeled after Mocha and works with Mocha's global testing methods. RuleTester can also be modified to work with other testing frameworks.
* `lib/source-code/` - this module is `SourceCode` class that is used to represent the parsed source code. It takes in source code and the Program node of the AST representing the code.
* `lib/rules/` - this contains built-in rules that verify source code.

## The `cli` object

Expand All @@ -31,15 +36,14 @@ This object may not:

## The `CLIEngine` object

The `CLIEngine` type represents the core functionality of the CLI except that it reads nothing from the command line and doesn't output anything by default. Instead, it accepts many (but not all) of the arguments that are passed into the CLI. It reads both configuration and source files as well as managing the environment that is passed into the `eslint` object.
The `CLIEngine` type represents the core functionality of the CLI except that it reads nothing from the command line and doesn't output anything by default. Instead, it accepts many (but not all) of the arguments that are passed into the CLI. It reads both configuration and source files as well as managing the environment that is passed into the `Linter` object.

The main method of the `CLIEngine` is `executeOnFiles()`, which accepts an array of file and directory names to run the linter on.

This object's responsibilities include:

* Managing the execution environment for `eslint`
* Managing the execution environment for `Linter`
* Reading from the file system
* Loading rule definitions
* Reading configuration information from config files (including `.eslintrc` and `package.json`)

This object may not:
Expand All @@ -49,11 +53,11 @@ This object may not:
* Output to the console
* Use formatters

## The `eslint` object
## The `Linter` object

The main method of the `eslint` object is `verify()` and accepts two arguments: the source text to verify and a configuration object (the baked configuration of the given configuration file plus command line options). The method first parses the given text with `espree` (or whatever the configured parser is) and retrieves the AST. The AST is produced with both line/column and range locations which are useful for reporting location of issues and retrieving the source text related to an AST node, respectively.
The main method of the `Linter` object is `verify()` and accepts two arguments: the source text to verify and a configuration object (the baked configuration of the given configuration file plus command line options). The method first parses the given text with `espree` (or whatever the configured parser is) and retrieves the AST. The AST is produced with both line/column and range locations which are useful for reporting location of issues and retrieving the source text related to an AST node, respectively.

Once the AST is available, `estraverse` is used to traverse the AST from top to bottom. At each node, the `eslint` object emits an event that has the same name as the node type (i.e., "Identifier", "WithStatement", etc.). On the way back up the subtree, an event is emitted with the AST type name and suffixed with ":exit", such as "Identifier:exit" - this allows rules to take action both on the way down and on the way up in the traversal. Each event is emitted with the appropriate AST node available.
Once the AST is available, `estraverse` is used to traverse the AST from top to bottom. At each node, the `Linter` object emits an event that has the same name as the node type (i.e., "Identifier", "WithStatement", etc.). On the way back up the subtree, an event is emitted with the AST type name and suffixed with ":exit", such as "Identifier:exit" - this allows rules to take action both on the way down and on the way up in the traversal. Each event is emitted with the appropriate AST node available.

This object's responsibilities include:

Expand Down
52 changes: 52 additions & 0 deletions docs/developer-guide/architecture/dependency.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions karma.conf.js
Expand Up @@ -19,7 +19,7 @@ module.exports = function(config) {
// list of files / patterns to load in the browser
files: [
"build/eslint.js",
"tests/lib/linter.js"
"tests/lib/linter/linter.js"
],


Expand All @@ -33,7 +33,7 @@ module.exports = function(config) {
* available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
*/
preprocessors: {
"tests/lib/linter.js": ["webpack"]
"tests/lib/linter/linter.js": ["webpack"]
},
webpack: {
mode: "none",
Expand Down
10 changes: 7 additions & 3 deletions lib/api.js
Expand Up @@ -5,15 +5,19 @@

"use strict";

const { CLIEngine } = require("./cli-engine");
const { Linter } = require("./linter");
const { RuleTester } = require("./rule-tester");
const { SourceCode } = require("./source-code");

module.exports = {
Linter,
CLIEngine: require("./cli-engine").CLIEngine,
RuleTester: require("./testers/rule-tester"),
SourceCode: require("./util/source-code")
CLIEngine,
RuleTester,
SourceCode
};

// DOTO: remove deprecated API.
let deprecatedLinterInstance = null;

Object.defineProperty(module.exports, "linter", {
Expand Down

0 comments on commit 219aecb

Please sign in to comment.