Skip to content

Commit

Permalink
feat: add filename property to the rule context (#17108)
Browse files Browse the repository at this point in the history
Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
  • Loading branch information
snitin315 and mdjermanovic committed Apr 29, 2023
1 parent 559ff4e commit 52018f2
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 15 deletions.
3 changes: 2 additions & 1 deletion docs/src/extend/custom-rules.md
Expand Up @@ -124,6 +124,7 @@ As the name implies, the `context` object contains information that is relevant
The `context` object has the following properties:

* `id`: (`string`) The rule ID.
* `filename`: (`string`) The filename associated with the source.
* `options`: (`array`) An array of the [configured options](../use/configure/rules) for this rule. This array does not include the rule severity (see the [dedicated section](#accessing-options-passed-to-a-rule)).
* `settings`: (`object`) The [shared settings](../use/configure/configuration-files#adding-shared-settings) from the configuration.
* `parserPath`: (`string`) The name of the `parser` from the configuration.
Expand All @@ -144,7 +145,7 @@ Additionally, the `context` object has the following methods:
* If the node is an `ImportDeclaration`, variables for all of its specifiers are returned.
* If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
* Otherwise, if the node does not declare any variables, an empty array is returned.
* `getFilename()`: Returns the filename associated with the source.
* `getFilename()`: (**Deprecated:** Use `context.filename` instead.) Returns the filename associated with the source.
* `getPhysicalFilename()`: When linting a file, it returns the full path of the file on disk without any code block information. When linting text, it returns the value passed to `—stdin-filename` or `<text>` if not specified.
* `getScope()`: (**Deprecated:** Use `SourceCode#getScope(node)` instead.) Returns the [scope](./scope-manager-interface#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
* `getSourceCode()`: Returns a `SourceCode` object that you can use to work with the source that was passed to ESLint (see [Accessing the Source Code](#accessing-the-source-code)).
Expand Down
3 changes: 2 additions & 1 deletion lib/linter/linter.js
Expand Up @@ -592,7 +592,7 @@ function findEslintEnv(text) {
* Convert "/path/to/<text>" to "<text>".
* `CLIEngine#executeOnText()` method gives "/path/to/<text>" if the filename
* was omitted because `configArray.extractConfig()` requires an absolute path.
* But the linter should pass `<text>` to `RuleContext#getFilename()` in that
* But the linter should pass `<text>` to `RuleContext#filename` in that
* case.
* Also, code blocks can have their virtual filename. If the parent filename was
* `<text>`, the virtual filename is `<text>/0_foo.js` or something like (i.e.,
Expand Down Expand Up @@ -952,6 +952,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserName, languageO
getDeclaredVariables: node => sourceCode.getDeclaredVariables(node),
getCwd: () => cwd,
getFilename: () => filename,
filename,
getPhysicalFilename: () => physicalFilename || filename,
getScope: () => sourceCode.getScope(currentNode),
getSourceCode: () => sourceCode,
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/testers/rule-tester/no-test-filename
Expand Up @@ -17,7 +17,7 @@ module.exports = {
create(context) {
return {
"Program": function(node) {
if (context.getFilename() === '<input>') {
if (context.filename === '<input>') {
context.report(node, "Filename test was not defined.");
}
}
Expand Down
89 changes: 77 additions & 12 deletions tests/lib/linter/linter.js
Expand Up @@ -1826,7 +1826,8 @@ describe("Linter", () => {
linter.defineRule(code, {
create: context => ({
Literal(node) {
context.report(node, context.getFilename());
assert.strictEqual(context.getFilename(), context.filename);
context.report(node, context.filename);
}
})
});
Expand Down Expand Up @@ -1866,7 +1867,8 @@ describe("Linter", () => {
linter.defineRule(code, {
create: context => ({
Literal(node) {
context.report(node, context.getFilename());
assert.strictEqual(context.getFilename(), context.filename);
context.report(node, context.filename);
}
})
});
Expand Down Expand Up @@ -4817,7 +4819,7 @@ var a = "test2";
describe("filenames", () => {
it("should allow filename to be passed on options object", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "foo.js");
assert.strictEqual(context.filename, "foo.js");
return {};
});

Expand All @@ -4828,7 +4830,7 @@ var a = "test2";

it("should allow filename to be passed as third argument", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "bar.js");
assert.strictEqual(context.filename, "bar.js");
return {};
});

Expand All @@ -4839,7 +4841,7 @@ var a = "test2";

it("should default filename to <input> when options object doesn't have filename", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "<input>");
assert.strictEqual(context.filename, "<input>");
return {};
});

Expand All @@ -4850,7 +4852,7 @@ var a = "test2";

it("should default filename to <input> when only two arguments are passed", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "<input>");
assert.strictEqual(context.filename, "<input>");
return {};
});

Expand Down Expand Up @@ -6751,7 +6753,7 @@ var a = "test2";
linter.defineRule("report-original-text", {
create: context => ({
Program(ast) {
receivedFilenames.push(context.getFilename());
receivedFilenames.push(context.filename);
receivedPhysicalFilenames.push(context.getPhysicalFilename());
context.report({ node: ast, message: context.getSourceCode().text });
}
Expand Down Expand Up @@ -9109,6 +9111,69 @@ describe("Linter with FlatConfigArray", () => {
});
});

describe("context.filename", () => {
const ruleId = "filename-rule";

it("has access to the filename", () => {

const config = {
plugins: {
test: {
rules: {
[ruleId]: {
create: context => ({
Literal(node) {
assert.strictEqual(context.getFilename(), context.filename);
context.report(node, context.filename);
}
})
}
}
}
},
rules: {
[`test/${ruleId}`]: 1
}
};

const messages = linter.verify("0", config, filename);
const suppressedMessages = linter.getSuppressedMessages();

assert.strictEqual(messages[0].message, filename);
assert.strictEqual(suppressedMessages.length, 0);
});

it("defaults filename to '<input>'", () => {

const config = {
plugins: {
test: {
rules: {
[ruleId]: {
create: context => ({
Literal(node) {
assert.strictEqual(context.getFilename(), context.filename);
context.report(node, context.filename);
}
})
}
}
}
},
rules: {
[`test/${ruleId}`]: 1
}
};


const messages = linter.verify("0", config);
const suppressedMessages = linter.getSuppressedMessages();

assert.strictEqual(messages[0].message, "<input>");
assert.strictEqual(suppressedMessages.length, 0);
});
});

describe("context.getPhysicalFilename()", () => {

const ruleId = "filename-rule";
Expand Down Expand Up @@ -11063,7 +11128,7 @@ describe("Linter with FlatConfigArray", () => {
describe("filename", () => {
it("should allow filename to be passed on options object", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "foo.js");
assert.strictEqual(context.filename, "foo.js");
return {};
});

Expand All @@ -11086,7 +11151,7 @@ describe("Linter with FlatConfigArray", () => {

it("should allow filename to be passed as third argument", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "bar.js");
assert.strictEqual(context.filename, "bar.js");
return {};
});

Expand All @@ -11109,7 +11174,7 @@ describe("Linter with FlatConfigArray", () => {

it("should default filename to <input> when options object doesn't have filename", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "<input>");
assert.strictEqual(context.filename, "<input>");
return {};
});

Expand All @@ -11132,7 +11197,7 @@ describe("Linter with FlatConfigArray", () => {

it("should default filename to <input> when only two arguments are passed", () => {
const filenameChecker = sinon.spy(context => {
assert.strictEqual(context.getFilename(), "<input>");
assert.strictEqual(context.filename, "<input>");
return {};
});

Expand Down Expand Up @@ -15369,7 +15434,7 @@ var a = "test2";
create(context) {
return {
Program(ast) {
receivedFilenames.push(context.getFilename());
receivedFilenames.push(context.filename);
receivedPhysicalFilenames.push(context.getPhysicalFilename());
context.report({ node: ast, message: context.getSourceCode().text });
}
Expand Down

0 comments on commit 52018f2

Please sign in to comment.