Skip to content

Commit 1ed1e8f

Browse files
authored
Restore previous approach to glint-* directives atop new TS Plugin architecture (#888)
1 parent 6e222ff commit 1ed1e8f

File tree

7 files changed

+69
-909
lines changed

7 files changed

+69
-909
lines changed

packages/core/src/transform/diagnostics/augmentation.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import type ts from 'typescript';
22
import { Diagnostic } from './index.js';
33
import GlimmerASTMappingTree, { MappingSource } from '../template/glimmer-ast-mapping-tree.js';
4-
4+
import TransformedModule from '../template/transformed-module.js';
55
export function augmentDiagnostics<T extends Diagnostic>(
6-
transformedModule: any,
6+
transformedModule: TransformedModule,
77
diagnostics: T[],
88
): T[] {
99
const mappingForDiagnostic = (diagnostic: ts.Diagnostic): GlimmerASTMappingTree | null => {
@@ -26,8 +26,31 @@ export function augmentDiagnostics<T extends Diagnostic>(
2626
return rangeWithMappingAndSource.mapping || null;
2727
};
2828

29-
// @ts-expect-error not sure how to fix
30-
return diagnostics.map((diagnostic) => rewriteMessageText(diagnostic, mappingForDiagnostic));
29+
const augmentedDiagnostics: T[] = [];
30+
31+
for (const diagnostic of diagnostics) {
32+
const augmentedDiagnostic = rewriteMessageText(diagnostic, mappingForDiagnostic);
33+
34+
const mapping = mappingForDiagnostic(diagnostic);
35+
36+
if (mapping) {
37+
const appliedDirective = transformedModule.directives.find(
38+
(directive) =>
39+
directive.areaOfEffect.start <= augmentedDiagnostic.start! &&
40+
directive.areaOfEffect.end > augmentedDiagnostic.start!,
41+
);
42+
43+
if (appliedDirective) {
44+
// Filter out this diagnostic; its covered by a directive.
45+
continue;
46+
}
47+
}
48+
49+
// @ts-expect-error not sure how to fix
50+
augmentedDiagnostics.push(augmentedDiagnostic);
51+
}
52+
53+
return augmentedDiagnostics;
3154
}
3255

3356
type DiagnosticHandler<T extends Diagnostic> = (

packages/core/src/transform/index.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,3 @@ export type { Directive, default as TransformedModule } from './template/transfo
22
export type { Diagnostic } from './diagnostics/index.js';
33

44
export { rewriteModule } from './template/rewrite-module.js';
5-
// export { rewriteDiagnostic, createTransformDiagnostic } from './diagnostics/index.js';

packages/core/src/transform/template/inlining/tagged-strings.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,15 @@ export function calculateTaggedTemplateSpans(
9191
}
9292

9393
if (transformedTemplate.result) {
94+
for (let { kind, location, areaOfEffect } of transformedTemplate.result.directives) {
95+
directives.push({
96+
kind: kind,
97+
source: script,
98+
location: addOffset(location, templateLocation.start),
99+
areaOfEffect: addOffset(areaOfEffect, templateLocation.start),
100+
});
101+
}
102+
94103
partialSpans.push({
95104
originalFile: script,
96105
originalStart: templateLocation.start,

packages/core/src/transform/template/map-template-contents.ts

Lines changed: 14 additions & 141 deletions
Original file line numberDiff line numberDiff line change
@@ -29,16 +29,17 @@ export type Mapper = {
2929
*/
3030
rangeForNode: (node: AST.Node, span?: AST.Node['loc']) => Range;
3131

32+
/**
33+
* Given a 0-based line number, returns the corresponding start and
34+
* end offsets for that line.
35+
*/
36+
rangeForLine: (line: number) => Range;
37+
3238
/**
3339
* Captures the existence of a directive specified by the given source
3440
* node and affecting the given range of text.
3541
*/
36-
directive: (
37-
commentNode: AST.CommentStatement | AST.MustacheCommentStatement,
38-
type: DirectiveKind,
39-
) => void;
40-
41-
// directiveTerminatingExpression: (location: Range) => void;
42+
directive: (type: DirectiveKind, location: Range, areaOfEffect: Range) => void;
4243

4344
/**
4445
* Records an error at the given location.
@@ -87,14 +88,6 @@ export type Mapper = {
8788
callback: () => void,
8889
codeFeaturesForNode?: CodeInformation,
8990
): void;
90-
91-
/**
92-
* This needs to be called after any node that "consumes" a `glint-expect-error` directive.
93-
* This essentially marks the end of the area of effect for the directive; this helps us
94-
* filter out the "unused ts-expect-error" placeholder diagnostic if, in fact, an error
95-
* diagnostic was reported within the directive's area of effect.
96-
*/
97-
terminateDirectiveAreaOfEffect(endStr: string): void;
9891
};
9992

10093
type LocalDirective = Omit<Directive, 'source'>;
@@ -184,13 +177,6 @@ export function mapTemplateContents(
184177
});
185178

186179
let ignoreErrors = false;
187-
let isNoCheckDirectivePresent = false;
188-
let expectErrorToken:
189-
| {
190-
numErrors: number;
191-
commentNode: AST.CommentStatement | AST.MustacheCommentStatement;
192-
}
193-
| undefined;
194180

195181
// Associates all content emitted during the given callback with the
196182
// given range in the template source and corresponding AST node.
@@ -269,23 +255,6 @@ export function mapTemplateContents(
269255
verification: false,
270256
};
271257
}
272-
273-
if (expectErrorToken) {
274-
// We are currently in a region of code covered by a @glint-expect-error directive. We need to
275-
// keep track of the number of errors encountered within this region so that we can know whether
276-
// we will need to propagate an "unused ts-expect-error" diagnostic back to the original
277-
// .gts file or not.
278-
const token = expectErrorToken;
279-
return {
280-
...features,
281-
verification: {
282-
shouldReport: () => {
283-
token.numErrors++;
284-
return false;
285-
},
286-
},
287-
};
288-
}
289258
}
290259
return features;
291260
}
@@ -335,112 +304,16 @@ export function mapTemplateContents(
335304
errors.push({ message, location });
336305
},
337306

338-
directive(
339-
commentNode: AST.CommentStatement | AST.MustacheCommentStatement,
340-
kind: DirectiveKind,
341-
) {
342-
if (kind === 'expect-error') {
343-
if (!expectErrorToken) {
344-
mapper.text(`// @glint-expect-error BEGIN AREA_OF_EFFECT`);
345-
mapper.newline();
346-
}
347-
348-
expectErrorToken = {
349-
numErrors: 0,
350-
commentNode,
351-
};
352-
}
353-
354-
if (kind === 'ignore') {
355-
ignoreErrors = true;
356-
mapper.text(`// @glint-ignore BEGIN AREA_OF_EFFECT`);
357-
mapper.newline();
358-
}
359-
360-
if (kind === 'nocheck') {
361-
ignoreErrors = true;
362-
isNoCheckDirectivePresent = true;
363-
mapper.text(`// @glint-nocheck`);
364-
mapper.newline();
365-
}
366-
367-
directives.push({ kind });
368-
},
369-
370-
terminateDirectiveAreaOfEffect(endStr: string) {
371-
if (expectErrorToken) {
372-
// There is an active "@glint-expect-error" directive whose
373-
// are of effect we need to terminate.
374-
//
375-
// There is a somewhat delicate order in which everything below needs to happen,
376-
// but here is an outline:
377-
//
378-
// 1. Volar will call the `shouldReport` function of the `verification` object
379-
// of the `CodeInformation` object that we pass along with each mapping for
380-
// every diagnostic reported by TS within the transformed region of code.
381-
//
382-
// 2. This callback's main job is to return a boolean indicating whether we
383-
// should propagate TS diagnostics within the transformed region of code
384-
// back to search (e.g. the original .gts file). But in addition to that we are somewhat
385-
// hackishly using `shouldReport` to track the number of errors encountered
386-
// within the directive's area of effect so that we can later determine
387-
// whether to filter out the "unused ts-expect-error" placeholder diagnostic
388-
// that we emit below.
389-
//
390-
// 3. The first `shouldReport` that gets called by Volar is in `resolveCodeFeatures`;
391-
// this implementation of `shouldReport` increments `numErrors` for each diagnostic
392-
// found in the region.
393-
//
394-
// 4. The second `shouldReport` that gets called is below: we emit a
395-
// `// @ts-expect-error GLINT_PLACEHOLDER` diagnostic that is always triggering
396-
// within the transformed code, and we use `shouldReport` to decide whether
397-
// to filter out that diagnostic or not.
398-
//
399-
// This approach was taken from Vue tooling; it is complicated but it solves the problem
400-
// of keeping the code transform static while keeping all of the dynamic/stateful
401-
// error tracking and filtering logic in `shouldReport` callbacks.
402-
const token = expectErrorToken;
403-
404-
mapper.newline();
405-
406-
// 1. Emit a ts-expect-error this is guaranteed to trigger within the generated TS code
407-
// because we immediately follow it up with an empty semi-colon statement.
408-
// 2. Map it back to the original `{{ @glint-expect-error }}` comment node in the source.
409-
mapper.forNode(
410-
token.commentNode,
411-
() => {
412-
mapper.text(`// @ts-expect-error GLINT_PLACEHOLDER`);
413-
},
414-
{
415-
verification: {
416-
// If no diagnostic errors were encountered within the area of effect,
417-
// then filter out the "unused ts-expect-error" diagnostic triggered by our
418-
// placeholder @ts-expect-error
419-
shouldReport: () => token.numErrors === 0,
420-
},
421-
},
422-
);
423-
424-
// Make the above placeholder diagnostic trigger an "unused ts-expect-error" diagnostic
425-
// by introducing an error-less empty semi-colon statement.
426-
mapper.newline();
427-
mapper.text(';');
428-
mapper.newline();
429-
430-
expectErrorToken = undefined;
431-
432-
mapper.text(`// @glint-expect-error END AREA_OF_EFFECT for ${endStr}`);
433-
mapper.newline();
434-
}
435-
436-
if (ignoreErrors && !isNoCheckDirectivePresent) {
437-
ignoreErrors = false;
438-
mapper.text(`// @glint-ignore END AREA_OF_EFFECT for ${endStr}`);
439-
mapper.newline();
440-
}
307+
directive(kind: DirectiveKind, location: Range, areaOfEffect: Range) {
308+
directives.push({ kind, location, areaOfEffect });
441309
},
442310

443311
rangeForNode: buildRangeForNode(lineOffsets),
312+
313+
rangeForLine: (line: number): Range => ({
314+
start: lineOffsets[line],
315+
end: lineOffsets[line + 1] ?? template.length,
316+
}),
444317
};
445318

446319
callback(ast, mapper);

0 commit comments

Comments
 (0)