@@ -7,6 +7,103 @@ import type { TemplateCodegenOptions } from './index';
77
88export  type  TemplateCodegenContext  =  ReturnType < typeof  createTemplateCodegenContext > ; 
99
10+ /** 
11+  * Creates and returns a Context object used for generating type-checkable TS code 
12+  * from the template section of a .vue file. 
13+  * 
14+  * ## Implementation Notes for supporting `@vue-ignore`, `@vue-expect-error`, and `@vue-skip` directives. 
15+  * 
16+  * Vue language tooling supports a number of directives for suppressing diagnostics within 
17+  * Vue templates (https://github.com/vuejs/language-tools/pull/3215) 
18+  * 
19+  * Here is an overview for how support for how @vue-expect-error is implemented within this file 
20+  * (@vue-expect-error is the most complicated directive to support due to its behavior of raising 
21+  * a diagnostic when it is annotating a piece of code that doesn't actually have any errors/warning/diagnostics). 
22+  * 
23+  * Given .vue code: 
24+  *  
25+  * ```vue 
26+  *   <script setup lang="ts"> 
27+  *   defineProps<{ 
28+  *     knownProp1: string; 
29+  *     knownProp2: string; 
30+  *     knownProp3: string; 
31+  *     knownProp4_will_trigger_unused_expect_error: string; 
32+  *   }>(); 
33+  *   </script> 
34+  *    
35+  *   <template> 
36+  *     {{ knownProp1 }} 
37+  *     {{ error_unknownProp }} <!-- ERROR: Property 'error_unknownProp' does not exist on type [...] --> 
38+  *     {{ knownProp2 }} 
39+  *     <!-- @vue-expect-error This suppresses an Unknown Property Error --> 
40+  *     {{ suppressed_error_unknownProp }} 
41+  *     {{ knownProp3 }} 
42+  *     <!-- @vue-expect-error This will trigger Unused '@ts-expect-error' directive.ts(2578) --> 
43+  *     {{ knownProp4_will_trigger_unused_expect_error }} 
44+  *   </template> 
45+  * ``` 
46+  *  
47+  * The above code should raise two diagnostics: 
48+  *  
49+  * 1. Property 'error_unknownProp' does not exist on type [...] 
50+  * 2. Unused '@ts-expect-error' directive.ts(2578) -- this is the bottom `@vue-expect-error` directive 
51+  *    that covers code that doesn't actually raise an error -- note that all `@vue-...` directives 
52+  *    will ultimately translate into `@ts-...` diagnostics. 
53+  *  
54+  * The above code will produce the following type-checkable TS code (note: omitting asterisks 
55+  * to prevent VSCode syntax double-greying out double-commented code). 
56+  *  
57+  * ```ts 
58+  *   ( __VLS_ctx.knownProp1 ); 
59+  *   ( __VLS_ctx.error_unknownProp ); // ERROR: Property 'error_unknownProp' does not exist on type [...] 
60+  *   ( __VLS_ctx.knownProp2 ); 
61+  *   // @vue -expect-error start 
62+  *   ( __VLS_ctx.suppressed_error_unknownProp ); 
63+  *   // @ts -expect-error __VLS_TS_EXPECT_ERROR 
64+  *   ; 
65+  *   // @vue -expect-error end of INTERPOLATION 
66+  *   ( __VLS_ctx.knownProp3 ); 
67+  *   // @vue -expect-error start 
68+  *   ( __VLS_ctx.knownProp4_will_trigger_unused_expect_error ); 
69+  *   // @ts -expect-error __VLS_TS_EXPECT_ERROR 
70+  *   ; 
71+  *   // @vue -expect-error end of INTERPOLATION 
72+  * ``` 
73+  * 
74+  * In the generated code, there are actually 3 diagnostic errors that'll be raised in the first 
75+  * pass on this generated code (but through cleverness described below, not all of them will be 
76+  * propagated back to the original .vue file): 
77+  *  
78+  * 1. Property 'error_unknownProp' does not exist on type [...] 
79+  * 2. Unused '@ts-expect-error' directive.ts(2578) from the 1st `@ts-expect-error __VLS_TS_EXPECT_ERROR` 
80+  * 3. Unused '@ts-expect-error' directive.ts(2578) from the 2nd `@ts-expect-error __VLS_TS_EXPECT_ERROR` 
81+  *  
82+  * Be sure to pay careful attention to the mixture of `@vue-expect-error` and `@ts-expect-error`; 
83+  * Within the TS file, the only "real" directives recognized by TS are going to be prefixed with `@ts-`; 
84+  * any `@vue-` prefixed directives in the comments are only for debugging purposes. 
85+  * 
86+  * As mentioned above, there are 3 diagnostics errors that'll be generated for the above code, but 
87+  * only 2 should be propagated back to the original .vue file. 
88+  *  
89+  * (The reason we structure things this way is somewhat complicated, but in short it allows us 
90+  * to lean on TS as much as possible to generate actual `unused @ts-expect-error directive` errors 
91+  * while covering a number of edge cases.) 
92+  *  
93+  * So, we need a way to dynamically decide whether each of the `@ts-expect-error __VLS_TS_EXPECT_ERROR` 
94+  * directives should be reported as an unused directive or not. 
95+  *  
96+  * To do this, we'll make use of the `shouldReport` callback that'll optionally be provided to the 
97+  * `verification` property of the `CodeInformation` object attached to the mapping between source .vue 
98+  * and generated .ts code. The `verification` property determines whether "verification" (which includes 
99+  * semantic diagnostics) should be performed on the generated .ts code, and `shouldReport`, if provided, 
100+  * can be used to determine whether a given diagnostic should be reported back "upwards" to the original 
101+  * .vue file or not. 
102+  *  
103+  * See the comments in the code below for how and where we use this hook to keep track of whether 
104+  * an error/diagnostic was encountered for a region of code covered by a `@vue-expect-error` directive, 
105+  * and additionally how we use that to determine whether to propagate diagnostics back upward. 
106+  */ 
10107export  function  createTemplateCodegenContext ( options : Pick < TemplateCodegenOptions ,  'scriptSetupBindingNames'  |  'edited' > )  { 
11108	let  ignoredError  =  false ; 
12109	let  expectErrorToken : { 
@@ -22,12 +119,18 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
22119	function  resolveCodeFeatures ( features : VueCodeInformation )  { 
23120		if  ( features . verification )  { 
24121			if  ( ignoredError )  { 
122+ 				// We are currently in a region of code covered by a @vue -ignore directive, so don't 
123+ 				// even bother performing any type-checking: set verification to false. 
25124				return  { 
26125					...features , 
27126					verification : false , 
28127				} ; 
29128			} 
30129			if  ( expectErrorToken )  { 
130+ 				// We are currently in a region of code covered by a @vue -expect-error directive. We need to 
131+ 				// keep track of the number of errors encountered within this region so that we can know whether 
132+ 				// we will need to propagate an "unused ts-expect-error" diagnostic back to the original 
133+ 				// .vue file or not. 
31134				const  token  =  expectErrorToken ; 
32135				return  { 
33136					...features , 
@@ -161,6 +264,9 @@ export function createTemplateCodegenContext(options: Pick<TemplateCodegenOption
161264					expectErrorToken . node . loc . end . offset , 
162265					{ 
163266						verification : { 
267+ 							// If no errors/warnings/diagnostics were reported within the region of code covered 
268+ 							// by the @vue -expect-error directive, then we should allow any `unused @ts-expect-error` 
269+ 							// diagnostics to be reported upward. 
164270							shouldReport : ( )  =>  token . errors  ===  0 , 
165271						} , 
166272					} , 
0 commit comments