8000 New: add `getPhysicalFilename()` method to rule context (fixes #11989… · eslint/eslint@bb66a3d · GitHub
[go: up one dir, main page]

Skip to content

Commit bb66a3d

Browse files
New: add getPhysicalFilename() method to rule context (fixes #11989) (#14616)
* New: add `getPhysicalFilename()` method to the rule context object * Docs: update * Chore: add test * Chore: update more instances * Chore: apply suggestions * Chore: apply suggestions * Chore: fix typo Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com> Co-authored-by: Milos Djermanovic <milos.djermanovic@gmail.com>
1 parent 2e43dac commit bb66a3d

File tree

3 files changed

+69
-4
lines changed

3 files changed

+69
-4
lines changed

docs/developer-guide/working-with-rules.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ Additionally, the `context` object has the following methods:
139139
* If the node is an `ImportSpecifier`, `ImportDefaultSpecifier`, or `ImportNamespaceSpecifier`, the declared variable is returned.
140140
* Otherwise, if the node does not declare any variables, an empty array is returned.
141141
* `getFilename()` - returns the filename associated with the source.
142+
* `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.
142143
* `getScope()` - returns the [scope](./scope-manager-interface.md#scope-interface) of the currently-traversed node. This information can be used to track references to variables.
143144
* `getSourceCode()` - returns a [`SourceCode`](#contextgetsourcecode) object that you can use to work with the source that was passed to ESLint.
144145
* `markVariableAsUsed(name)` - marks a variable with the given name in the current scope as used. This affects the [no-unused-vars](../rules/no-unused-vars.md) rule. Returns `true` if a variable with the given name was found and marked as used, otherwise `false`.

lib/linter/linter.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -828,9 +828,10 @@ const BASE_TRAVERSAL_CONTEXT = Object.freeze(
828828
* @param {string} filename The reported filename of the code
829829
* @param {boolean} disableFixes If true, it doesn't make `fix` properties.
830830
* @param {string | undefined} cwd cwd of the cli
831+
* @param {string} physicalFilename The full path of the file on disk without any code block information
831832
* @returns {Problem[]} An array of reported problems
832833
*/
833-
function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd) {
834+
function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parserName, settings, filename, disableFixes, cwd, physicalFilename) {
834835
const emitter = createEmitter();
835836
const nodeQueue = [];
836837
let currentNode = sourceCode.ast;
@@ -859,6 +860,7 @@ function runRules(sourceCode, configuredRules, ruleMapper, parserOptions, parser
859860
getDeclaredVariables: sourceCode.scopeManager.getDeclaredVariables.bind(sourceCode.scopeManager),
860861
getCwd: () => cwd,
861862
getFilename: () => filename,
863+
getPhysicalFilename: () => physicalFilename || filename,
862864
getScope: () => getScope(sourceCode.scopeManager, currentNode),
863865
getSourceCode: () => sourceCode,
864866
markVariableAsUsed: name => markVariableAsUsed(sourceCode.scopeManager, currentNode, parserOptions, name),
@@ -1181,7 +1183,8 @@ class Linter {
11811183
settings,
11821184
options.filename,
11831185
options.disableFixes,
1184-
slots.cwd
1186+
slots.cwd,
1187+
providedOptions.physicalFilename
11851188
);
11861189
} catch (err) {
11871190
err.message += `\nOccurred while linting ${options.filename}`;
@@ -1284,6 +1287,7 @@ class Linter {
12841287
_verifyWithProcessor(textOrSourceCode, config, options, configForRecursive) {
12851288
const filename = options.filename || "<input>";
12861289
const filenameToExpose = normalizeFilename(filename);
1290+
const physicalFilename = options.physicalFilename || filenameToExpose;
12871291
const text = ensureText(textOrSourceCode);
12881292
const preprocess = options.preprocess || (rawText => [rawText]);
12891293

@@ -1316,15 +1320,15 @@ class Linter {
13161320
return this._verifyWithConfigArray(
13171321
blockText,
13181322
configForRecursive,
1319-
{ ...options, filename: blockName }
1323+
{ ...options, filename: blockName, physicalFilename }
13201324
);
13211325
}
13221326

13231327
// Does lint.
13241328
return this._verifyWithoutProcessors(
13251329
blockText,
13261330
config,
1327-
{ ...options, filename: blockName }
1331+
{ ...options, filename: blockName, physicalFilename }
13281332
);
13291333
});
13301334

tests/lib/linter/linter.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,22 @@ describe("Linter", () => {
15591559
assert.strictEqual(messages[0].message, filename);
15601560
});
15611561

1562+
it("has access to the physicalFilename", () => {
1563+
linter.defineRule(code, context => ({
1564+
Literal(node) {
1565+
context.report(node, context.getPhysicalFilename());
1566+
}
1567+
}));
1568+
1569+
const config = { rules: {} };
1570+
1571+
config.rules[code] = 1;
1572+
1573+
const messages = linter.verify("0", config, filename);
1574+
1575+
assert.strictEqual(messages[0].message, filename);
1576+
});
1577+
15621578
it("defaults filename to '<input>'", () => {
15631579
linter.defineRule(code, context => ({
15641580
Literal(node) {
@@ -3408,6 +3424,41 @@ var a = "test2";
34083424
});
34093425
});
34103426

3427+
describe("physicalFilenames", () => {
3428+
it("should be same as `filename` passed on options object, if no processors are used", () => {
3429+
const physicalFilenameChecker = sinon.spy(context => {
3430+
assert.strictEqual(context.getPhysicalFilename(), "foo.js");
3431+
return {};
3432+
});
3433+
3434+
linter.defineRule("checker", physicalFilenameChecker);
3435+
linter.verify("foo;", { rules: { checker: "error" } }, { filename: "foo.js" });
3436+
assert(physicalFilenameChecker.calledOnce);
3437+
});
3438+
3439+
it("should default physicalFilename to <input> when options object doesn't have filename", () => {
3440+
const physicalFilenameChecker = sinon.spy(context => {
3441+
assert.strictEqual(context.getPhysicalFilename(), "<input>");
3442+
return {};
3443+
});
3444+
3445+
linter.defineRule("checker", physicalFilenameChecker);
3446+
linter.verify("foo;", { rules: { checker: "error" } }, {});
3447+
assert(physicalFilenameChecker.calledOnce);
3448+
});
3449+
3450+
it("should default physicalFilename to <input> when only two arguments are passed", () => {
3451+
const physicalFilenameChecker = sinon.spy(context => {
3452+
assert.strictEqual(context.getPhysicalFilename(), "<input>");
3453+
return {};
3454+
});
3455+
3456+
linter.defineRule("checker", physicalFilenameChecker);
3457+
linter.verify("foo;", { rules: { checker: "error" } });
3458+
assert(physicalFilenameChecker.calledOnce);
3459+
});
3460+
});
3461+
34113462
it("should report warnings in order by line and column when called", () => {
34123463

34133464
const code = "foo()\n alert('test')";
@@ -4783,14 +4834,17 @@ var a = "test2";
47834834

47844835
describe("processors", () => {
47854836
let receivedFilenames = [];
4837+
let receivedPhysicalFilenames = [];
47864838

47874839
beforeEach(() => {
47884840
receivedFilenames = [];
4841+
receivedPhysicalFilenames = [];
47894842

47904843
// A rule that always reports the AST with a message equal to the source text
47914844
linter.defineRule("report-original-text", context => ({
47924845
Program(ast) {
47934846
receivedFilenames.push(context.getFilename());
4847+
receivedPhysicalFilenames.push(context.getPhysicalFilename());
47944848
context.report({ node: ast, message: context.getSourceCode().text });
47954849
}
47964850
}));
@@ -4845,10 +4899,16 @@ var a = "test2";
48454899

48464900
assert.strictEqual(problems.length, 3);
48474901
assert.deepStrictEqual(problems.map(problem => problem.message), ["foo", "bar", "baz"]);
4902+
4903+
// filename
48484904
assert.strictEqual(receivedFilenames.length, 3);
48494905
assert(/^filename\.js[/\\]0_block\.js/u.test(receivedFilenames[0]));
48504906
assert(/^filename\.js[/\\]1_block\.js/u.test(receivedFilenames[1]));
48514907
assert(/^filename\.js[/\\]2_block\.js/u.test(receivedFilenames[2]));
4908+
4909+
// physical filename
4910+
assert.strictEqual(receivedPhysicalFilenames.length, 3);
4911+
assert.strictEqual(receivedPhysicalFilenames.every(name => name === filename), true);
48524912
});
48534913

48544914
it("should receive text even if a SourceCode object was given.", () => {

0 commit comments

Comments
 (0)
0