Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(compiler): Add extended diagnostic to warn when missing let on n…
…gForOf In the case that a user accidentally forgot the let keyword, they dont get a very clear indicator of there being a problem. They get an issue in the template iteration at runtime. This diagnostic will warn the user when the let keyword is missing.
- Loading branch information
1 parent
db631be
commit c3eca5d
Showing
10 changed files
with
183 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
17 changes: 17 additions & 0 deletions
17
packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_ngforof_let/BUILD.bazel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
load("//tools:defaults.bzl", "ts_library") | ||
|
||
ts_library( | ||
name = "missing_ngforof_let", | ||
srcs = ["index.ts"], | ||
visibility = [ | ||
"//packages/compiler-cli/src/ngtsc:__subpackages__", | ||
"//packages/compiler-cli/test/ngtsc:__pkg__", | ||
], | ||
deps = [ | ||
"//packages/compiler", | ||
"//packages/compiler-cli/src/ngtsc/diagnostics", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/api", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/api", | ||
"@npm//typescript", | ||
], | ||
) |
43 changes: 43 additions & 0 deletions
43
packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_ngforof_let/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import {AST, TmplAstNode, TmplAstTemplate} from '@angular/compiler'; | ||
import ts from 'typescript'; | ||
|
||
import {ErrorCode, ExtendedTemplateDiagnosticName} from '../../../../diagnostics'; | ||
import {NgTemplateDiagnostic} from '../../../api'; | ||
import {TemplateCheckFactory, TemplateCheckWithVisitor, TemplateContext} from '../../api'; | ||
|
||
/** | ||
* Ensures a user doesn't forget to omit `let` when using ngfor. | ||
* Will return diagnostic information when `let` is missing. | ||
*/ | ||
class MissingNgForOfLetCheck extends TemplateCheckWithVisitor<ErrorCode.MISSING_NGFOROF_LET> { | ||
override code = ErrorCode.MISSING_NGFOROF_LET as const; | ||
|
||
override visitNode( | ||
ctx: TemplateContext<ErrorCode.MISSING_NGFOROF_LET>, component: ts.ClassDeclaration, | ||
node: TmplAstNode|AST): NgTemplateDiagnostic<ErrorCode.MISSING_NGFOROF_LET>[] { | ||
const isTemplate = node instanceof TmplAstTemplate; | ||
if (!(isTemplate) || (isTemplate && !(node.templateAttrs.length))) return []; | ||
const attr = node.templateAttrs.find(x => x.name === 'ngForOf'); | ||
if (attr == null) return []; | ||
|
||
if (node.variables.length > 0) return []; | ||
const errorString = 'Your ngForOf is missing a value. Did you forget to add the `let` keyword?'; | ||
const diagnostic = ctx.makeTemplateDiagnostic(node.sourceSpan, errorString); | ||
return [diagnostic]; | ||
} | ||
} | ||
|
||
export const factory: TemplateCheckFactory< | ||
ErrorCode.MISSING_NGFOROF_LET, ExtendedTemplateDiagnosticName.MISSING_NGFOROF_LET> = { | ||
code: ErrorCode.MISSING_NGFOROF_LET, | ||
name: ExtendedTemplateDiagnosticName.MISSING_NGFOROF_LET, | ||
create: () => new MissingNgForOfLetCheck(), | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
...ges/compiler-cli/src/ngtsc/typecheck/extended/test/checks/missing_ngforof_let/BUILD.bazel
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library") | ||
|
||
ts_library( | ||
name = "test_lib", | ||
testonly = True, | ||
srcs = ["missing_ngforof_let_spec.ts"], | ||
deps = [ | ||
"//packages/compiler", | ||
"//packages/compiler-cli/src/ngtsc/core:api", | ||
"//packages/compiler-cli/src/ngtsc/diagnostics", | ||
"//packages/compiler-cli/src/ngtsc/file_system", | ||
"//packages/compiler-cli/src/ngtsc/file_system/testing", | ||
"//packages/compiler-cli/src/ngtsc/testing", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/extended", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/extended/checks/missing_ngforof_let", | ||
"//packages/compiler-cli/src/ngtsc/typecheck/testing", | ||
"@npm//typescript", | ||
], | ||
) | ||
|
||
jasmine_node_test( | ||
name = "test", | ||
bootstrap = ["//tools/testing:node_no_angular_es2015"], | ||
deps = [ | ||
":test_lib", | ||
], | ||
) |
66 changes: 66 additions & 0 deletions
66
.../src/ngtsc/typecheck/extended/test/checks/missing_ngforof_let/missing_ngforof_let_spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/** | ||
* @license | ||
* Copyright Google LLC All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
|
||
import ts from 'typescript'; | ||
|
||
import {ErrorCode, ExtendedTemplateDiagnosticName, ngErrorCode} from '../../../../../diagnostics'; | ||
import {absoluteFrom, getSourceFileOrError} from '../../../../../file_system'; | ||
import {runInEachFileSystem} from '../../../../../file_system/testing'; | ||
import {getSourceCodeForDiagnostic} from '../../../../../testing'; | ||
import {getClass, setup} from '../../../../testing'; | ||
import {factory as missingNgForOfLet} from '../../../checks/missing_ngforof_let/index'; | ||
import {ExtendedTemplateCheckerImpl} from '../../../src/extended_template_checker'; | ||
|
||
runInEachFileSystem(() => { | ||
describe('TemplateChecks', () => { | ||
it('binds the error code to its extended template diagnostic name', () => { | ||
expect(missingNgForOfLet.code).toBe(ErrorCode.MISSING_NGFOROF_LET); | ||
expect(missingNgForOfLet.name).toBe(ExtendedTemplateDiagnosticName.MISSING_NGFOROF_LET); | ||
}); | ||
|
||
it('should produce missing ngforof let warning', () => { | ||
const fileName = absoluteFrom('/main.ts'); | ||
const {program, templateTypeChecker} = setup([{ | ||
fileName, | ||
templates: { | ||
'TestCmp': '<ul><li *ngFor="thing of items">{{thing["name"]}}</li></ul>', | ||
}, | ||
source: | ||
'export class TestCmp { items: [] = [{\'name\': \'diana\'}, {\'name\': \'prince\'}] }' | ||
}]); | ||
const sf = getSourceFileOrError(program, fileName); | ||
const component = getClass(sf, 'TestCmp'); | ||
const extendedTemplateChecker = new ExtendedTemplateCheckerImpl( | ||
templateTypeChecker, program.getTypeChecker(), [missingNgForOfLet], {} /* options */); | ||
const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); | ||
expect(diags.length).toBe(1); | ||
expect(diags[0].category).toBe(ts.DiagnosticCategory.Warning); | ||
expect(diags[0].code).toBe(ngErrorCode(ErrorCode.MISSING_NGFOROF_LET)); | ||
expect(getSourceCodeForDiagnostic(diags[0])) | ||
.toBe('<li *ngFor="thing of items">{{thing["name"]}}</li>'); | ||
}); | ||
|
||
it('should not produce missing ngforof let warning if written correctly', () => { | ||
const fileName = absoluteFrom('/main.ts'); | ||
const {program, templateTypeChecker} = setup([{ | ||
fileName, | ||
templates: { | ||
'TestCmp': '<ul><li *ngFor="let item of items">{{item["name"]}};</li></ul>', | ||
}, | ||
source: | ||
'export class TestCmp { items: [] = [{\'name\': \'diana\'}, {\'name\': \'prince\'}] }' | ||
}]); | ||
const sf = getSourceFileOrError(program, fileName); | ||
const component = getClass(sf, 'TestCmp'); | ||
const extendedTemplateChecker = new ExtendedTemplateCheckerImpl( | ||
templateTypeChecker, program.getTypeChecker(), [missingNgForOfLet], {} /* options */); | ||
const diags = extendedTemplateChecker.getDiagnosticsForComponent(component); | ||
expect(diags.length).toBe(0); | ||
}); | ||
}); | ||
}); |