Skip to content

Commit dd407bd

Browse files
authored
refactor: minor tweak to transformTemplateBindings (#276)
1 parent 5f6e0cd commit dd407bd

File tree

3 files changed

+191
-193
lines changed

3 files changed

+191
-193
lines changed

src/transform-microsyntax.ts

Lines changed: 143 additions & 152 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,71 @@ import type {
1717
RawNGSpan,
1818
} from './types.js';
1919
import { NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX } from './parser.js';
20-
import { toLowerCamelCase, transformSpan } from './utils.js';
20+
import { toLowerCamelCase, transformSpan, createNode } from './utils.js';
21+
22+
function isExpressionBinding(
23+
templateBinding: ng.TemplateBinding,
24+
): templateBinding is ng.ExpressionBinding {
25+
return templateBinding instanceof NGExpressionBinding;
26+
}
27+
28+
function isVariableBinding(
29+
templateBinding: ng.TemplateBinding,
30+
): templateBinding is ng.VariableBinding {
31+
return templateBinding instanceof NGVariableBinding;
32+
}
33+
34+
/**
35+
* - "a" (start=0 end=1) -> (start=0 end=3)
36+
* - '\'' (start=0 end=1) -> (start=0 end=4)
37+
*/
38+
function fixSpan(span: RawNGSpan, text: string) {
39+
if (text[span.start] !== '"' && text[span.start] !== "'") {
40+
return;
41+
}
42+
const quote = text[span.start];
43+
let hasBackSlash = false;
44+
for (let i = span.start + 1; i < text.length; i++) {
45+
switch (text[i]) {
46+
case quote:
47+
if (!hasBackSlash) {
48+
span.end = i + 1;
49+
return;
50+
}
51+
// fall through
52+
default:
53+
hasBackSlash = false;
54+
break;
55+
case '\\':
56+
hasBackSlash = !hasBackSlash;
57+
break;
58+
}
59+
}
60+
}
61+
62+
/**
63+
* - "as b" (value="NgEstreeParser" key="b") -> (value="$implicit" key="b")
64+
*/
65+
function getAsVariableBindingValue(
66+
variableBinding: ng.VariableBinding,
67+
context: Context,
68+
): ng.VariableBinding['value'] {
69+
if (
70+
!variableBinding.value ||
71+
variableBinding.value.source !== NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX
72+
) {
73+
return variableBinding.value;
74+
}
75+
76+
const index = context.getCharacterIndex(
77+
/\S/,
78+
variableBinding.sourceSpan.start,
79+
);
80+
return {
81+
source: '$implicit',
82+
span: { start: index, end: index },
83+
};
84+
}
2185

2286
function transformTemplateBindings({
2387
expressions: rawTemplateBindings,
@@ -53,11 +117,11 @@ function transformTemplateBindings({
53117
templateBinding.value &&
54118
templateBinding.value.source === lastTemplateBinding.key.source
55119
) {
56-
const alias = _c<NGMicrosyntaxKey>(
57-
'NGMicrosyntaxKey',
58-
{ name: templateBinding.key.source },
59-
templateBinding.key.span,
60-
);
120+
const alias = _c<NGMicrosyntaxKey>({
121+
type: 'NGMicrosyntaxKey',
122+
name: templateBinding.key.source,
123+
...templateBinding.key.span,
124+
});
61125
const updateSpanEnd = <T extends NGNode>(node: T, end: number): T => ({
62126
...node,
63127
...transformSpan({ start: node.start!, end }, context.text),
@@ -84,13 +148,13 @@ function transformTemplateBindings({
84148
lastTemplateBinding = templateBinding;
85149
}
86150

87-
return _c<NGMicrosyntax>(
88-
'NGMicrosyntax',
89-
{ body },
90-
body.length === 0
151+
return _c<NGMicrosyntax>({
152+
type: 'NGMicrosyntax',
153+
body,
154+
...(body.length === 0
91155
? rawTemplateBindings[0].sourceSpan
92-
: { start: body[0].start, end: body.at(-1)!.end },
93-
);
156+
: { start: body[0].start, end: body.at(-1)!.end }),
157+
});
94158

95159
function transformTemplateBinding(
96160
templateBinding: ng.TemplateBinding,
@@ -99,34 +163,35 @@ function transformTemplateBindings({
99163
if (isExpressionBinding(templateBinding)) {
100164
const { key, value } = templateBinding;
101165
if (!value) {
102-
return _c<NGMicrosyntaxKey>(
103-
'NGMicrosyntaxKey',
104-
{ name: removePrefix(key.source) },
105-
key.span,
106-
);
166+
return _c<NGMicrosyntaxKey>({
167+
type: 'NGMicrosyntaxKey',
168+
name: removePrefix(key.source),
169+
...key.span,
170+
});
107171
} else if (index === 0) {
108-
return _c<NGMicrosyntaxExpression>(
109-
'NGMicrosyntaxExpression',
110-
{ expression: _t<NGNode>(value.ast), alias: null },
111-
value.sourceSpan,
112-
);
172+
return _c<NGMicrosyntaxExpression>({
173+
type: 'NGMicrosyntaxExpression',
174+
expression: _t<NGNode>(value.ast),
175+
alias: null,
176+
...value.sourceSpan,
177+
});
113178
} else {
114-
return _c<NGMicrosyntaxKeyedExpression>(
115-
'NGMicrosyntaxKeyedExpression',
116-
{
117-
key: _c<NGMicrosyntaxKey>(
118-
'NGMicrosyntaxKey',
119-
{ name: removePrefix(key.source) },
120-
key.span,
121-
),
122-
expression: _c<NGMicrosyntaxExpression>(
123-
'NGMicrosyntaxExpression',
124-
{ expression: _t<NGNode>(value.ast), alias: null },
125-
value.sourceSpan,
126-
),
127-
},
128-
{ start: key.span.start, end: value.sourceSpan.end },
129-
);
179+
return _c<NGMicrosyntaxKeyedExpression>({
180+
type: 'NGMicrosyntaxKeyedExpression',
181+
key: _c<NGMicrosyntaxKey>({
182+
type: 'NGMicrosyntaxKey',
183+
name: removePrefix(key.source),
184+
...key.span,
185+
}),
186+
expression: _c<NGMicrosyntaxExpression>({
187+
type: 'NGMicrosyntaxExpression',
188+
expression: _t<NGNode>(value.ast),
189+
alias: null,
190+
...value.sourceSpan,
191+
}),
192+
start: key.span.start,
193+
end: value.sourceSpan.end,
194+
});
130195
}
131196
} else {
132197
const { key, sourceSpan } = templateBinding;
@@ -135,138 +200,64 @@ function transformTemplateBindings({
135200
);
136201
if (startsWithLet) {
137202
const { value } = templateBinding;
138-
return _c<NGMicrosyntaxLet>(
139-
'NGMicrosyntaxLet',
140-
{
141-
key: _c<NGMicrosyntaxKey>(
142-
'NGMicrosyntaxKey',
143-
{ name: key.source },
144-
key.span,
145-
),
146-
value: !value
147-
? null
148-
: _c<NGMicrosyntaxKey>(
149-
'NGMicrosyntaxKey',
150-
{ name: value.source },
151-
value.span,
152-
),
153-
},
154-
{
155-
start: sourceSpan.start,
156-
end: value ? value.span.end : key.span.end,
157-
},
158-
);
203+
return _c<NGMicrosyntaxLet>({
204+
type: 'NGMicrosyntaxLet',
205+
key: _c<NGMicrosyntaxKey>({
206+
type: 'NGMicrosyntaxKey',
207+
name: key.source,
208+
...key.span,
209+
}),
210+
value: !value
211+
? null
212+
: _c<NGMicrosyntaxKey>({
213+
type: 'NGMicrosyntaxKey',
214+
name: value.source,
215+
...value.span,
216+
}),
217+
start: sourceSpan.start,
218+
end: value ? value.span.end : key.span.end,
219+
});
159220
} else {
160-
const value = getAsVariableBindingValue(templateBinding);
161-
return _c<NGMicrosyntaxAs>(
162-
'NGMicrosyntaxAs',
163-
{
164-
key: _c<NGMicrosyntaxKey>(
165-
'NGMicrosyntaxKey',
166-
{ name: value!.source },
167-
value!.span,
168-
),
169-
alias: _c<NGMicrosyntaxKey>(
170-
'NGMicrosyntaxKey',
171-
{ name: key.source },
172-
key.span,
173-
),
174-
},
175-
{ start: value!.span.start, end: key.span.end },
176-
);
221+
const value = getAsVariableBindingValue(templateBinding, context);
222+
return _c<NGMicrosyntaxAs>({
223+
type: 'NGMicrosyntaxAs',
224+
key: _c<NGMicrosyntaxKey>({
225+
type: 'NGMicrosyntaxKey',
226+
name: value!.source,
227+
...value!.span,
228+
}),
229+
alias: _c<NGMicrosyntaxKey>({
230+
type: 'NGMicrosyntaxKey',
231+
name: key.source,
232+
...key.span,
233+
}),
234+
start: value!.span.start,
235+
end: key.span.end,
236+
});
177237
}
178238
}
179239
}
180240

181-
function _t<T extends NGNode>(n: ng.AST) {
182-
return transformNode(n, context) as T;
241+
function _t<T extends NGNode>(node: ng.AST) {
242+
return transformNode(node, context) as T;
183243
}
184244

185245
function _c<T extends NGNode>(
186-
t: T['type'],
187-
n: Partial<T>,
188-
span: RawNGSpan,
189-
stripSpaces = true,
246+
properties: Partial<T> & { type: T['type'] } & RawNGSpan,
247+
{ stripSpaces = true } = {},
190248
) {
191-
return {
192-
type: t,
193-
...transformSpan(span, context.text, { processSpan: stripSpaces }),
194-
...n,
195-
} as T;
249+
return createNode<T>(context, properties, { stripSpaces });
196250
}
197251

198252
function removePrefix(string: string) {
199253
return toLowerCamelCase(string.slice(prefix.source.length));
200254
}
201255

202-
function isExpressionBinding(
203-
templateBinding: ng.TemplateBinding,
204-
): templateBinding is ng.ExpressionBinding {
205-
return templateBinding instanceof NGExpressionBinding;
206-
}
207-
208-
function isVariableBinding(
209-
templateBinding: ng.TemplateBinding,
210-
): templateBinding is ng.VariableBinding {
211-
return templateBinding instanceof NGVariableBinding;
212-
}
213-
214256
function fixTemplateBindingSpan(templateBinding: ng.TemplateBinding) {
215-
fixSpan(templateBinding.key.span);
257+
fixSpan(templateBinding.key.span, context.text);
216258
if (isVariableBinding(templateBinding) && templateBinding.value) {
217-
fixSpan(templateBinding.value.span);
218-
}
219-
}
220-
221-
/**
222-
* - "a" (start=0 end=1) -> (start=0 end=3)
223-
* - '\'' (start=0 end=1) -> (start=0 end=4)
224-
*/
225-
function fixSpan(span: RawNGSpan) {
226-
if (context.text[span.start] !== '"' && context.text[span.start] !== "'") {
227-
return;
259+
fixSpan(templateBinding.value.span, context.text);
228260
}
229-
const quote = context.text[span.start];
230-
let hasBackSlash = false;
231-
for (let i = span.start + 1; i < context.text.length; i++) {
232-
switch (context.text[i]) {
233-
case quote:
234-
if (!hasBackSlash) {
235-
span.end = i + 1;
236-
return;
237-
}
238-
// fall through
239-
default:
240-
hasBackSlash = false;
241-
break;
242-
case '\\':
243-
hasBackSlash = !hasBackSlash;
244-
break;
245-
}
246-
}
247-
}
248-
249-
/**
250-
* - "as b" (value="NgEstreeParser" key="b") -> (value="$implicit" key="b")
251-
*/
252-
function getAsVariableBindingValue(
253-
variableBinding: ng.VariableBinding,
254-
): ng.VariableBinding['value'] {
255-
if (
256-
!variableBinding.value ||
257-
variableBinding.value.source !== NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX
258-
) {
259-
return variableBinding.value;
260-
}
261-
262-
const index = context.getCharacterIndex(
263-
/\S/,
264-
variableBinding.sourceSpan.start,
265-
);
266-
return {
267-
source: '$implicit',
268-
span: { start: index, end: index },
269-
};
270261
}
271262
}
272263

0 commit comments

Comments
 (0)