diff --git a/scripts/eslint/rules/jsdoc-format.cjs b/scripts/eslint/rules/jsdoc-format.cjs index 4753fe9cc84cc..9e38ca0f99c29 100644 --- a/scripts/eslint/rules/jsdoc-format.cjs +++ b/scripts/eslint/rules/jsdoc-format.cjs @@ -11,6 +11,7 @@ module.exports = createRule({ internalCommentNotLastError: `@internal should only appear in final JSDoc comment for declaration.`, multipleJSDocError: `Declaration has multiple JSDoc comments.`, internalCommentOnParameterProperty: `@internal cannot appear on a JSDoc comment; use a declared property and an assignment in the constructor instead.`, + internalCommentOnUnexported: `@internal should not appear on an unexported declaration.`, }, schema: [], type: "problem", @@ -23,6 +24,31 @@ module.exports = createRule({ const atInternal = "@internal"; const jsdocStart = "/**"; + /** @type {Map} */ + const isExportedCache = new Map(); + + /** @type {(node: import("@typescript-eslint/utils").TSESTree.Node) => boolean} */ + function isExported(node) { + const exported = isExportedCache.get(node); + if (exported !== undefined) { + return exported; + } + + /** @type {import("@typescript-eslint/utils").TSESTree.Node | undefined} */ + let current = node; + while (current) { + // https://github.com/typescript-eslint/typescript-eslint/blob/e44a1a280f08f9fd0d29f74e5c3e73b7b64a9606/packages/eslint-plugin/src/util/collectUnusedVariables.ts#L440 + if (current.type.startsWith("Export")) { + isExportedCache.set(node, true); + return true; + } + isExportedCache.set(current, false); + current = current.parent; + } + + return false; + } + /** @type {(text: string) => boolean} */ function isJSDocText(text) { return text.startsWith(jsdocStart); @@ -81,12 +107,15 @@ module.exports = createRule({ if (!isJSDoc) { context.report({ messageId: "internalCommentInNonJSDocError", node: c, loc: getAtInternalLoc(c, indexInComment) }); } - else if (i !== last) { - context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) }); - } else if (node.type === "TSParameterProperty") { context.report({ messageId: "internalCommentOnParameterProperty", node: c, loc: getAtInternalLoc(c, indexInComment) }); } + else if (!isExported(node)) { + context.report({ messageId: "internalCommentOnUnexported", node: c, loc: getAtInternalLoc(c, indexInComment) }); + } + else if (i !== last) { + context.report({ messageId: "internalCommentNotLastError", node: c, loc: getAtInternalLoc(c, indexInComment) }); + } } }; diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts index 7b0d1ebb87d35..4521fb0600cd2 100644 --- a/src/compiler/scanner.ts +++ b/src/compiler/scanner.ts @@ -2805,13 +2805,11 @@ export function createScanner(languageVersion: ScriptTarget, skipTrivia: boolean } } -/** @internal */ function codePointAt(s: string, i: number): number { // TODO(jakebailey): this is wrong and should have ?? 0; but all users are okay with it return s.codePointAt(i)!; } -/** @internal */ function charSize(ch: number) { if (ch >= 0x10000) { return 2; diff --git a/src/services/services.ts b/src/services/services.ts index f97d3eb0651dc..18f0b4d151439 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -787,7 +787,7 @@ class IdentifierObject extends TokenOrIdentifierObject im declare _declarationBrand: any; declare _jsdocContainerBrand: any; declare _flowContainerBrand: any; - /** @internal */ typeArguments!: NodeArray; + typeArguments!: NodeArray; constructor(kind: SyntaxKind.Identifier, pos: number, end: number) { super(kind, pos, end); }