Skip to content

Commit 815e150

Browse files
authored
feat(eslint-plugin-fluid): Add rule to forbid the use of relative file path links in JSDoc/TSDoc comments (#25500)
Such links are not portable and will cause problems for external users. Stable, externally accessible link targets should be used instead (for example, other APIs or GitHub URLs).
1 parent c69d56d commit 815e150

File tree

5 files changed

+171
-0
lines changed

5 files changed

+171
-0
lines changed

common/build/eslint-plugin-fluid/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ New rules added:
77
- `@fluid-internal/fluid/no-markdown-links-in-jsdoc`: Forbids the use of Markdown link syntax in JSDoc/TSDoc comments.
88
- Such links are not supported by TSDoc spec and are not supported by some of our tooling.
99
`{@link}` syntax should be used instead.
10+
- `@fluid-internal/fluid/no-file-path-links-in-jsdoc`: Forbids the use of file paths in `{@link}` tags in JSDoc/TSDoc comments.
11+
- Such links are not portable and will cause problems for external users.
12+
Stable, externally accessible link targets should be used instead (for example, other APIs or GitHub URLs).
1013

1114
## [0.1.6](https://github.com/microsoft/FluidFramework/releases/tag/eslint-plugin-fluid_v0.1.6)
1215

common/build/eslint-plugin-fluid/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,23 @@
1212
*/
1313
module.exports = {
1414
rules: {
15+
/**
16+
* Disallow file path links in JSDoc/TSDoc comments.
17+
* Full name: "@fluid-internal/fluid/no-file-path-links-in-jsdoc"
18+
*/
19+
"no-file-path-links-in-jsdoc": require("./src/rules/no-file-path-links-in-jsdoc"),
20+
1521
/**
1622
* Disallow Markdown link syntax in JSDoc/TSDoc comments.
1723
* Full name: "@fluid-internal/fluid/no-markdown-links-in-jsdoc"
1824
*/
1925
"no-markdown-links-in-jsdoc": require("./src/rules/no-markdown-links-in-jsdoc"),
26+
2027
/**
2128
* Full name: "@fluid-internal/fluid/no-member-release-tags"
2229
*/
2330
"no-member-release-tags": require("./src/rules/no-member-release-tags"),
31+
2432
"no-restricted-tags-imports": require("./src/rules/no-restricted-tags-imports"),
2533
"no-unchecked-record-access": require("./src/rules/no-unchecked-record-access"),
2634
},
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
/**
7+
* File path links are not portable in the context of JSDoc/TSDoc comments.
8+
* For this reason, we disallow them in favor of links to stable, user-accessible resources (like GitHub URLs).
9+
* This rule enforces that file path links do not appear in `{@link}` tags in JSDoc/TSDoc comments.
10+
*
11+
* @remarks
12+
* Our separate rule, `no-markdown-links-in-jsdoc`, disallows Markdown link syntax in JSDoc/TSDoc comments.
13+
*/
14+
module.exports = {
15+
meta: {
16+
type: "problem",
17+
docs: {
18+
description: "Disallow relative path link syntax in comments",
19+
category: "Best Practices",
20+
recommended: false,
21+
},
22+
messages: {
23+
filePathLink:
24+
"File path links are not allowed in JSDoc/TSDoc comments. Link to a stable, user-accessible resource (like an API reference or a GitHub URL) instead.",
25+
},
26+
schema: [],
27+
},
28+
29+
create(context) {
30+
return {
31+
Program() {
32+
const sourceCode = context.getSourceCode();
33+
const comments = sourceCode
34+
.getAllComments()
35+
// Filter to only JSDoc/TSDoc style block comments
36+
.filter((comment) => comment.type === "Block" && comment.value.startsWith("*"));
37+
38+
for (const comment of comments) {
39+
// JSDoc/TSDoc link syntax: {@link target|text} or {@link target}
40+
// Find links where the `target` component is a file path (starts with `/`, `./`, or `../`)
41+
const matches = comment.value.matchAll(/{@link\s+(\/|\.\/|\.\.\/).*}/g);
42+
for (const match of matches) {
43+
const startIndex = comment.range[0] + match.index;
44+
const endIndex = startIndex + match[0].length;
45+
46+
context.report({
47+
loc: {
48+
start: sourceCode.getLocFromIndex(startIndex),
49+
end: sourceCode.getLocFromIndex(endIndex),
50+
},
51+
messageId: "filePathLink",
52+
});
53+
}
54+
}
55+
},
56+
};
57+
},
58+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
const assert = require("assert");
7+
const path = require("path");
8+
const { ESLint } = require("eslint");
9+
10+
describe("Do not allow file path links in JSDoc/TSDoc comments", function () {
11+
async function lintFile(file) {
12+
const eslint = new ESLint({
13+
useEslintrc: false,
14+
overrideConfig: {
15+
rules: {
16+
"no-file-path-links-in-jsdoc": "error",
17+
},
18+
parser: "@typescript-eslint/parser",
19+
parserOptions: {
20+
project: path.join(__dirname, "../example/tsconfig.json"),
21+
},
22+
},
23+
rulePaths: [path.join(__dirname, "../../rules")],
24+
});
25+
const fileToLint = path.join(__dirname, "../example/no-file-path-links-in-jsdoc", file);
26+
const results = await eslint.lintFiles([fileToLint]);
27+
assert.equal(results.length, 1, "Expected a single result for linting a single file.");
28+
return results[0];
29+
}
30+
31+
const expectedErrorMessage =
32+
"File path links are not allowed in JSDoc/TSDoc comments. Link to a stable, user-accessible resource (like an API reference or a GitHub URL) instead.";
33+
34+
it("Should report errors for file path links in block comments", async function () {
35+
const result = await lintFile("test.ts");
36+
assert.strictEqual(result.errorCount, 4);
37+
38+
// Error 1
39+
assert.strictEqual(result.messages[0].message, expectedErrorMessage);
40+
assert.strictEqual(result.messages[0].line, 10);
41+
42+
// Error 2
43+
assert.strictEqual(result.messages[1].message, expectedErrorMessage);
44+
assert.strictEqual(result.messages[1].line, 11);
45+
46+
// Error 3
47+
assert.strictEqual(result.messages[2].message, expectedErrorMessage);
48+
assert.strictEqual(result.messages[2].line, 16);
49+
50+
// Error 4
51+
assert.strictEqual(result.messages[3].message, expectedErrorMessage);
52+
assert.strictEqual(result.messages[3].line, 17);
53+
});
54+
});
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*!
2+
* Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3+
* Licensed under the MIT License.
4+
*/
5+
6+
// #region Invalid cases
7+
8+
// TSDoc comment with a relative path link should be flagged.
9+
/**
10+
* TSDoc comment with link using a relative file path: {@link ./relative/path|text}.
11+
* And another: {@link ../relative-path}.
12+
*/
13+
const tsdocCommentWithRelativePathLinks = "invalid";
14+
15+
/**
16+
* TSDoc comment with link using an absolute file path: {@link /absolute/path|text}.
17+
* And another: {@link /absolute-path}.
18+
*/
19+
const tsdocCommentWithAbsolutePathLinks = "invalid";
20+
21+
// #endregion
22+
23+
// #region Valid cases
24+
25+
// TSDoc comment with no links should not be flagged.
26+
/**
27+
* TSDoc comment with no links.
28+
*/
29+
const tsdocCommentWithNoLinks = "valid";
30+
31+
// TSDoc comment with a URL link should be allowed.
32+
/**
33+
* TSDoc comment with link with URL target: {@link https://bing.com|bing.com}.
34+
* And another: {@link https://fluidframework.com}.
35+
*/
36+
const tsdocCommentWithValidDocsLink = "valid";
37+
38+
// Block comment should not be flagged.
39+
/*
40+
* Block comment with link using a file path: {@link ./relative/path|text}.
41+
*/
42+
const blockCommentWithRelativePathLink = "valid";
43+
44+
// Line comment should not be flagged.
45+
// Line comment with link using a file path: {@link /absolute-path}.
46+
const lineCommentWithFilePathLink = "valid";
47+
48+
// #endregion

0 commit comments

Comments
 (0)