Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
New: pass cwd from cli engine (#12389)
* New: pass cwd from cli engine

* fix linting error
  • Loading branch information
fa93hws authored and kaicataldo committed Oct 16, 2019
1 parent b962775 commit 874fe16
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 6 deletions.
13 changes: 11 additions & 2 deletions docs/developer-guide/nodejs-api.md
Expand Up @@ -80,13 +80,22 @@ const codeLines = SourceCode.splitLines(code);

## Linter

The `Linter` object does the actual evaluation of the JavaScript code. It doesn't do any filesystem operations, it simply parses and reports on the code. In particular, the `Linter` object does not process configuration objects or files. You can retrieve instances of `Linter` like this:
The `Linter` object does the actual evaluation of the JavaScript code. It doesn't do any filesystem operations, it simply parses and reports on the code. In particular, the `Linter` object does not process configuration objects or files.
The `Linter` is a constructor, and you can create a new instance by passing in the options you want to use. The available options are:

* `cwd` - Path to a directory that should be considered as the current working directory. It is accessible to rules by calling `context.getCwd()` (see [The Context Object](./working-with-rules.md#The-Context-Object)). If `cwd` is `undefined`, it will be normalized to `process.cwd()` if the global `process` object is defined (for example, in the Node.js runtime) , or `undefined` otherwise.

For example:

```js
const Linter = require("eslint").Linter;
const linter = new Linter();
const linter1 = new Linter({ cwd: 'path/to/project' });
const linter2 = new Linter();
```

In this example, rules run on `linter1` will get `path/to/project` when calling `context.getCwd()`.
Those run on `linter2` will get `process.cwd()` if the global `process` object is defined or `undefined` otherwise (e.g. on the browser https://eslint.org/demo).

### Linter#verify

The most important method on `Linter` is `verify()`, which initiates linting of the given text. This method accepts three arguments:
Expand Down
1 change: 1 addition & 0 deletions docs/developer-guide/working-with-rules.md
Expand Up @@ -126,6 +126,7 @@ The `context` object contains additional functionality that is helpful for rules
Additionally, the `context` object has the following methods:

* `getAncestors()` - returns an array of the ancestors of the currently-traversed node, starting at the root of the AST and continuing through the direct parent of the current node. This array does not include the currently-traversed node itself.
* `getCwd()` - returns the `cwd` passed to [Linter](./nodejs-api.md#Linter). It is a path to a directory that should be considered as the current working directory.
* `getDeclaredVariables(node)` - returns a list of [variables](./scope-manager-interface.md#variable-interface) declared by the given node. This information can be used to track references to variables.
* If the node is a `VariableDeclaration`, all variables declared in the declaration are returned.
* If the node is a `VariableDeclarator`, all variables declared in the declarator are returned.
Expand Down
2 changes: 1 addition & 1 deletion lib/cli-engine/cli-engine.js
Expand Up @@ -567,7 +567,7 @@ class CLIEngine {
});
const lintResultCache =
options.cache ? new LintResultCache(cacheFilePath) : null;
const linter = new Linter();
const linter = new Linter({ cwd: options.cwd });

/** @type {ConfigArray[]} */
const lastConfigArrays = [configArrayFactory.getConfigArrayForFile()];
Expand Down
33 changes: 30 additions & 3 deletions lib/linter/linter.js
Expand Up @@ -810,9 +810,10 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
* @param {Object} settings The settings that were enabled in the config
* @param {string} filename The reported filename of the code
* @param {boolean} disableFixes If true, it doesn't make `fix` properties.
* @param {string | undefined} cwd cwd of the cli
* @returns {Problem[]} An array of reported problems
*/
function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes) {
function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd) {
const emitter = createEmitter();
const nodeQueue = [];
let currentNode = sourceCode.ast;
Expand All @@ -839,6 +840,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
{
getAncestors: () => getAncestors(currentNode),
getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
getCwd: () => cwd,
getFilename: () => filename,
getScope: () => getScope(sourceCode.scopeManager, currentNode),
getSourceCode: () => sourceCode,
Expand Down Expand Up @@ -985,6 +987,24 @@ function getRule(slots, ruleId) {
);
}

/**
* Normalize the value of the cwd
* @param {string | undefined} cwd raw value of the cwd, path to a directory that should be considered as the current working directory, can be undefined.
* @returns {string | undefined} normalized cwd
*/
function normalizeCwd(cwd) {
if (cwd) {
return cwd;
}
if (typeof process === "object") {
return process.cwd();
}

// It's more explicit to assign the undefined
// eslint-disable-next-line no-undefined
return undefined;
}

/**
* The map to store private data.
* @type {WeakMap<Linter, LinterInternalSlots>}
Expand All @@ -1001,8 +1021,14 @@ const internalSlotsMap = new WeakMap();
*/
class Linter {

constructor() {
/**
* Initialize the Linter.
* @param {Object} [config] the config object
* @param {string} [config.cwd] path to a directory that should be considered as the current working directory, can be undefined.
*/
constructor({ cwd } = {}) {
internalSlotsMap.set(this, {
cwd: normalizeCwd(cwd),
lastConfigArray: null,
lastSourceCode: null,
parserMap: new Map([["espree", espree]]),
Expand Down Expand Up @@ -1134,7 +1160,8 @@ class Linter {
parserName,
settings,
options.filename,
options.disableFixes
options.disableFixes,
slots.cwd
);
} catch (err) {
err.message += `\nOccurred while linting ${options.filename}`;
Expand Down
52 changes: 52 additions & 0 deletions tests/lib/linter/linter.js
Expand Up @@ -3195,6 +3195,58 @@ describe("Linter", () => {
});
});

describe("when receiving cwd in options during instantiation", () => {
const code = "a;\nb;";
const config = { rules: { checker: "error" } };

it("should get cwd correctly in the context", () => {
const cwd = "cwd";
const linterWithOption = new Linter({ cwd });
let spy;

linterWithOption.defineRule("checker", context => {
spy = sinon.spy(() => {
assert.strictEqual(context.getCwd(), cwd);
});
return { Program: spy };
});

linterWithOption.verify(code, config);
assert(spy && spy.calledOnce);
});

it("should assign process.cwd() to it if cwd is undefined", () => {
let spy;
const linterWithOption = new Linter({ });

linterWithOption.defineRule("checker", context => {

spy = sinon.spy(() => {
assert.strictEqual(context.getCwd(), process.cwd());
});
return { Program: spy };
});

linterWithOption.verify(code, config);
assert(spy && spy.calledOnce);
});

it("should assign process.cwd() to it if the option is undefined", () => {
let spy;

linter.defineRule("checker", context => {

spy = sinon.spy(() => {
assert.strictEqual(context.getCwd(), process.cwd());
});
return { Program: spy };
});

linter.verify(code, config);
assert(spy && spy.calledOnce);
});
});

describe("reportUnusedDisable option", () => {
it("reports problems for unused eslint-disable comments", () => {
assert.deepStrictEqual(
Expand Down

0 comments on commit 874fe16

Please sign in to comment.