Skip to content

Commit c2adf91

Browse files
NullVoxPopuliclaude
andcommitted
chore: fix lint errors in whitespace stripping logic
- Replace non-null assertions with ?. checks - Replace !! with Boolean() Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
1 parent 6e2754b commit c2adf91

4 files changed

Lines changed: 1144 additions & 9 deletions

File tree

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import type { Nullable } from '@glimmer/interfaces';
2+
import { asPresentArray, expect, getLast, localAssert, unwrap } from '@glimmer/debug-util';
3+
import { assign } from '@glimmer/util';
4+
import {
5+
EntityParser,
6+
EventedTokenizer,
7+
HTML5NamedCharRefs as namedCharRefs,
8+
} from 'simple-html-tokenizer';
9+
10+
import type * as src from './source/api';
11+
import type * as ASTv1 from './v1/api';
12+
import type * as HBS from './v1/handlebars-ast';
13+
14+
export type ParserNodeBuilder<N extends { loc: src.SourceSpan }> = Omit<N, 'loc'> & {
15+
start: src.SourceOffset;
16+
};
17+
18+
export interface StartTag {
19+
readonly type: 'StartTag';
20+
name: string;
21+
nameStart: Nullable<src.SourceOffset>;
22+
nameEnd: Nullable<src.SourceOffset>;
23+
readonly attributes: ASTv1.AttrNode[];
24+
readonly modifiers: ASTv1.ElementModifierStatement[];
25+
readonly comments: ASTv1.MustacheCommentStatement[];
26+
readonly params: ASTv1.VarHead[];
27+
selfClosing: boolean;
28+
readonly loc: src.SourceSpan;
29+
}
30+
31+
export interface EndTag {
32+
readonly type: 'EndTag';
33+
name: string;
34+
readonly loc: src.SourceSpan;
35+
}
36+
37+
export interface Attribute {
38+
name: string;
39+
currentPart: ASTv1.TextNode | null;
40+
parts: (ASTv1.MustacheStatement | ASTv1.TextNode)[];
41+
isQuoted: boolean;
42+
isDynamic: boolean;
43+
start: src.SourceOffset;
44+
valueSpan: src.SourceSpan;
45+
}
46+
47+
export abstract class Parser {
48+
protected elementStack: ASTv1.ParentNode[] = [];
49+
private lines: string[];
50+
readonly source: src.Source;
51+
public currentAttribute: Nullable<Attribute> = null;
52+
public currentNode: Nullable<
53+
Readonly<
54+
| ParserNodeBuilder<ASTv1.CommentStatement>
55+
| ParserNodeBuilder<ASTv1.TextNode>
56+
| ParserNodeBuilder<StartTag>
57+
| ParserNodeBuilder<EndTag>
58+
>
59+
> = null;
60+
public tokenizer: EventedTokenizer;
61+
62+
constructor(
63+
source: src.Source,
64+
entityParser = new EntityParser(namedCharRefs),
65+
mode: 'precompile' | 'codemod' = 'precompile'
66+
) {
67+
this.source = source;
68+
this.lines = source.source.split(/\r\n?|\n/u);
69+
this.tokenizer = new EventedTokenizer(this, entityParser, mode);
70+
}
71+
72+
offset(): src.SourceOffset {
73+
let { line, column } = this.tokenizer;
74+
return this.source.offsetFor(line, column);
75+
}
76+
77+
pos({ line, column }: src.SourcePosition): src.SourceOffset {
78+
return this.source.offsetFor(line, column);
79+
}
80+
81+
finish<T extends { loc: src.SourceSpan }>(node: ParserNodeBuilder<T>): T {
82+
return assign({}, node, {
83+
loc: node.start.until(this.offset()),
84+
} as const) as unknown as T;
85+
86+
// node.loc = node.loc.withEnd(end);
87+
}
88+
89+
abstract parse(node: HBS.Program, locals: string[]): ASTv1.Template;
90+
91+
abstract Program(node: HBS.Program): HBS.Output<'Program'>;
92+
abstract MustacheStatement(node: HBS.MustacheStatement): HBS.Output<'MustacheStatement'>;
93+
abstract Decorator(node: HBS.Decorator): HBS.Output<'Decorator'>;
94+
abstract BlockStatement(node: HBS.BlockStatement): HBS.Output<'BlockStatement'>;
95+
abstract DecoratorBlock(node: HBS.DecoratorBlock): HBS.Output<'DecoratorBlock'>;
96+
abstract PartialStatement(node: HBS.PartialStatement): HBS.Output<'PartialStatement'>;
97+
abstract PartialBlockStatement(
98+
node: HBS.PartialBlockStatement
99+
): HBS.Output<'PartialBlockStatement'>;
100+
abstract ContentStatement(node: HBS.ContentStatement): HBS.Output<'ContentStatement'>;
101+
abstract CommentStatement(node: HBS.CommentStatement): HBS.Output<'CommentStatement'>;
102+
abstract SubExpression(node: HBS.SubExpression): HBS.Output<'SubExpression'>;
103+
abstract PathExpression(node: HBS.PathExpression): HBS.Output<'PathExpression'>;
104+
abstract StringLiteral(node: HBS.StringLiteral): HBS.Output<'StringLiteral'>;
105+
abstract BooleanLiteral(node: HBS.BooleanLiteral): HBS.Output<'BooleanLiteral'>;
106+
abstract NumberLiteral(node: HBS.NumberLiteral): HBS.Output<'NumberLiteral'>;
107+
abstract UndefinedLiteral(node: HBS.UndefinedLiteral): HBS.Output<'UndefinedLiteral'>;
108+
abstract NullLiteral(node: HBS.NullLiteral): HBS.Output<'NullLiteral'>;
109+
110+
abstract reset(): void;
111+
abstract finishData(): void;
112+
abstract tagOpen(): void;
113+
abstract beginData(): void;
114+
abstract appendToData(char: string): void;
115+
abstract beginStartTag(): void;
116+
abstract appendToTagName(char: string): void;
117+
abstract beginAttribute(): void;
118+
abstract appendToAttributeName(char: string): void;
119+
abstract beginAttributeValue(quoted: boolean): void;
120+
abstract appendToAttributeValue(char: string): void;
121+
abstract finishAttributeValue(): void;
122+
abstract markTagAsSelfClosing(): void;
123+
abstract beginEndTag(): void;
124+
abstract finishTag(): void;
125+
abstract beginComment(): void;
126+
abstract appendToCommentData(char: string): void;
127+
abstract finishComment(): void;
128+
abstract reportSyntaxError(error: string): void;
129+
130+
get currentAttr(): Attribute {
131+
return expect(this.currentAttribute, 'expected attribute');
132+
}
133+
134+
get currentTag(): ParserNodeBuilder<StartTag> | ParserNodeBuilder<EndTag> {
135+
let node = this.currentNode;
136+
localAssert(node && (node.type === 'StartTag' || node.type === 'EndTag'), 'expected tag');
137+
return node;
138+
}
139+
140+
get currentStartTag(): ParserNodeBuilder<StartTag> {
141+
let node = this.currentNode;
142+
localAssert(node && node.type === 'StartTag', 'expected start tag');
143+
return node;
144+
}
145+
146+
get currentEndTag(): ParserNodeBuilder<EndTag> {
147+
let node = this.currentNode;
148+
localAssert(node && node.type === 'EndTag', 'expected end tag');
149+
return node;
150+
}
151+
152+
get currentComment(): ParserNodeBuilder<ASTv1.CommentStatement> {
153+
let node = this.currentNode;
154+
localAssert(node && node.type === 'CommentStatement', 'expected a comment');
155+
return node;
156+
}
157+
158+
get currentData(): ParserNodeBuilder<ASTv1.TextNode> {
159+
let node = this.currentNode;
160+
localAssert(node && node.type === 'TextNode', 'expected a text node');
161+
return node;
162+
}
163+
164+
acceptNode<T extends HBS.NodeType>(node: HBS.Node<T>): HBS.Output<T> {
165+
return (this[node.type as T] as (node: HBS.Node<T>) => HBS.Output<T>)(node);
166+
}
167+
168+
currentElement(): ASTv1.ParentNode {
169+
return getLast(asPresentArray(this.elementStack));
170+
}
171+
172+
sourceForNode(node: HBS.Node, endNode?: { loc: HBS.SourceLocation }): string {
173+
let firstLine = node.loc.start.line - 1;
174+
let currentLine = firstLine - 1;
175+
let firstColumn = node.loc.start.column;
176+
let string = [];
177+
let line: string;
178+
179+
let lastLine: number;
180+
let lastColumn: number;
181+
182+
if (endNode) {
183+
lastLine = endNode.loc.end.line - 1;
184+
lastColumn = endNode.loc.end.column;
185+
} else {
186+
lastLine = node.loc.end.line - 1;
187+
lastColumn = node.loc.end.column;
188+
}
189+
190+
while (currentLine < lastLine) {
191+
currentLine++;
192+
line = unwrap(this.lines[currentLine]);
193+
194+
if (currentLine === firstLine) {
195+
if (firstLine === lastLine) {
196+
string.push(line.slice(firstColumn, lastColumn));
197+
} else {
198+
string.push(line.slice(firstColumn));
199+
}
200+
} else if (currentLine === lastLine) {
201+
string.push(line.slice(0, lastColumn));
202+
} else {
203+
string.push(line);
204+
}
205+
}
206+
207+
return string.join('\n');
208+
}
209+
}

0 commit comments

Comments
 (0)