diff --git a/src/services/textChanges.ts b/src/services/textChanges.ts index 0d45039f5b1..b9a13fc5420 100644 --- a/src/services/textChanges.ts +++ b/src/services/textChanges.ts @@ -1075,7 +1075,7 @@ namespace ts.textChanges { } export function isValidLocationToAddComment(sourceFile: SourceFile, position: number) { - return !isInComment(sourceFile, position) && !isInString(sourceFile, position) && !isInTemplateString(sourceFile, position); + return !isInComment(sourceFile, position) && !isInString(sourceFile, position) && !isInTemplateString(sourceFile, position) && !isInJSXText(sourceFile, position); } function needSemicolonBetween(a: Node, b: Node): boolean { diff --git a/src/services/utilities.ts b/src/services/utilities.ts index f0326599ad0..7a713fed749 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -909,6 +909,20 @@ namespace ts { return isTemplateLiteralKind(token.kind) && position > token.getStart(sourceFile); } + export function isInJSXText(sourceFile: SourceFile, position: number) { + const token = getTokenAtPosition(sourceFile, position); + if (isJsxText(token)) { + return true; + } + if (token.kind === SyntaxKind.OpenBraceToken && isJsxExpression(token.parent) && isJsxElement(token.parent.parent)) { + return true; + } + if (token.kind === SyntaxKind.LessThanToken && isJsxOpeningLikeElement(token.parent) && isJsxElement(token.parent.parent)) { + return true; + } + return false; + } + export function findPrecedingMatchingToken(token: Node, matchingTokenKind: SyntaxKind, sourceFile: SourceFile) { const tokenKind = token.kind; let remainingMatchingTokens = 0; diff --git a/tests/cases/fourslash/jsxTsIgnoreOnJSXExpressionsAndChildren.ts b/tests/cases/fourslash/jsxTsIgnoreOnJSXExpressionsAndChildren.ts new file mode 100644 index 00000000000..9670a88cc3d --- /dev/null +++ b/tests/cases/fourslash/jsxTsIgnoreOnJSXExpressionsAndChildren.ts @@ -0,0 +1,95 @@ +/// + +// @allowJs: true +// @checkJs: true +// @noEmit: true +// @jsx: react +// @esModuleInterop: true +// @skipLibCheck: true +// @Filename: declarations.d.ts +////declare namespace JSX { +//// interface Element {} +//// interface IntrinsicElements { [index: string]: {} } +////} +////declare var React: any; +// @Filename: MyComponent.jsx +////class MyComponent extends React.Component { +//// render() { +//// return ( +////
+//// {[|/*1*/doesNotExist|]} +////
+//// ); +//// } +////} +// @Filename: MyComponent2.jsx +////class MyComponent2 extends React.Component { +//// render() { +//// return ( +////
+//// Aleph{[|/*2*/doesNotExist|]}Bet +////
+//// ); +//// } +////} +// @Filename: MyComponent3.jsx +////class MyComponent3 extends React.Component { +//// render() { +//// return ( +////
+//// <[|/*3*/DoesNotExist|] /> +////
+//// ); +//// } +////} + +goTo.file(1); +verify.getSyntacticDiagnostics([]); +verify.getSemanticDiagnostics([{ code: 2304, message: "Cannot find name 'doesNotExist'." }]); +verify.codeFix({ + index: 0, + description: "Ignore this error message", + newFileContent: `class MyComponent extends React.Component { + render() { + return ( +
+ { +// @ts-ignore + doesNotExist} +
+ ); + } +}` +}); +goTo.file(2); +verify.codeFix({ + index: 0, + description: "Ignore this error message", + newFileContent: `class MyComponent2 extends React.Component { + render() { + return ( +
+ Aleph{ +// @ts-ignore + doesNotExist}Bet +
+ ); + } +}` +}); +goTo.file(3); +verify.codeFix({ + index: 0, + description: "Ignore this error message", + newFileContent: `class MyComponent3 extends React.Component { + render() { + return ( +
+ < +// @ts-ignore + DoesNotExist /> +
+ ); + } +}` +});