Skip to content

Commit

Permalink
refactor: minor tweak to transformTemplateBindings (#276)
Browse files Browse the repository at this point in the history
  • Loading branch information
fisker authored Nov 14, 2023
1 parent 5f6e0cd commit dd407bd
Show file tree
Hide file tree
Showing 3 changed files with 191 additions and 193 deletions.
295 changes: 143 additions & 152 deletions src/transform-microsyntax.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,71 @@ import type {
RawNGSpan,
} from './types.js';
import { NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX } from './parser.js';
import { toLowerCamelCase, transformSpan } from './utils.js';
import { toLowerCamelCase, transformSpan, createNode } from './utils.js';

function isExpressionBinding(
templateBinding: ng.TemplateBinding,
): templateBinding is ng.ExpressionBinding {
return templateBinding instanceof NGExpressionBinding;
}

function isVariableBinding(
templateBinding: ng.TemplateBinding,
): templateBinding is ng.VariableBinding {
return templateBinding instanceof NGVariableBinding;
}

/**
* - "a" (start=0 end=1) -> (start=0 end=3)
* - '\'' (start=0 end=1) -> (start=0 end=4)
*/
function fixSpan(span: RawNGSpan, text: string) {
if (text[span.start] !== '"' && text[span.start] !== "'") {
return;
}
const quote = text[span.start];
let hasBackSlash = false;
for (let i = span.start + 1; i < text.length; i++) {
switch (text[i]) {
case quote:
if (!hasBackSlash) {
span.end = i + 1;
return;
}
// fall through
default:
hasBackSlash = false;
break;
case '\\':
hasBackSlash = !hasBackSlash;
break;
}
}
}

/**
* - "as b" (value="NgEstreeParser" key="b") -> (value="$implicit" key="b")
*/
function getAsVariableBindingValue(
variableBinding: ng.VariableBinding,
context: Context,
): ng.VariableBinding['value'] {
if (
!variableBinding.value ||
variableBinding.value.source !== NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX
) {
return variableBinding.value;
}

const index = context.getCharacterIndex(
/\S/,
variableBinding.sourceSpan.start,
);
return {
source: '$implicit',
span: { start: index, end: index },
};
}

function transformTemplateBindings({
expressions: rawTemplateBindings,
Expand Down Expand Up @@ -53,11 +117,11 @@ function transformTemplateBindings({
templateBinding.value &&
templateBinding.value.source === lastTemplateBinding.key.source
) {
const alias = _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: templateBinding.key.source },
templateBinding.key.span,
);
const alias = _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: templateBinding.key.source,
...templateBinding.key.span,
});
const updateSpanEnd = <T extends NGNode>(node: T, end: number): T => ({
...node,
...transformSpan({ start: node.start!, end }, context.text),
Expand All @@ -84,13 +148,13 @@ function transformTemplateBindings({
lastTemplateBinding = templateBinding;
}

return _c<NGMicrosyntax>(
'NGMicrosyntax',
{ body },
body.length === 0
return _c<NGMicrosyntax>({
type: 'NGMicrosyntax',
body,
...(body.length === 0
? rawTemplateBindings[0].sourceSpan
: { start: body[0].start, end: body.at(-1)!.end },
);
: { start: body[0].start, end: body.at(-1)!.end }),
});

function transformTemplateBinding(
templateBinding: ng.TemplateBinding,
Expand All @@ -99,34 +163,35 @@ function transformTemplateBindings({
if (isExpressionBinding(templateBinding)) {
const { key, value } = templateBinding;
if (!value) {
return _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: removePrefix(key.source) },
key.span,
);
return _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: removePrefix(key.source),
...key.span,
});
} else if (index === 0) {
return _c<NGMicrosyntaxExpression>(
'NGMicrosyntaxExpression',
{ expression: _t<NGNode>(value.ast), alias: null },
value.sourceSpan,
);
return _c<NGMicrosyntaxExpression>({
type: 'NGMicrosyntaxExpression',
expression: _t<NGNode>(value.ast),
alias: null,
...value.sourceSpan,
});
} else {
return _c<NGMicrosyntaxKeyedExpression>(
'NGMicrosyntaxKeyedExpression',
{
key: _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: removePrefix(key.source) },
key.span,
),
expression: _c<NGMicrosyntaxExpression>(
'NGMicrosyntaxExpression',
{ expression: _t<NGNode>(value.ast), alias: null },
value.sourceSpan,
),
},
{ start: key.span.start, end: value.sourceSpan.end },
);
return _c<NGMicrosyntaxKeyedExpression>({
type: 'NGMicrosyntaxKeyedExpression',
key: _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: removePrefix(key.source),
...key.span,
}),
expression: _c<NGMicrosyntaxExpression>({
type: 'NGMicrosyntaxExpression',
expression: _t<NGNode>(value.ast),
alias: null,
...value.sourceSpan,
}),
start: key.span.start,
end: value.sourceSpan.end,
});
}
} else {
const { key, sourceSpan } = templateBinding;
Expand All @@ -135,138 +200,64 @@ function transformTemplateBindings({
);
if (startsWithLet) {
const { value } = templateBinding;
return _c<NGMicrosyntaxLet>(
'NGMicrosyntaxLet',
{
key: _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: key.source },
key.span,
),
value: !value
? null
: _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: value.source },
value.span,
),
},
{
start: sourceSpan.start,
end: value ? value.span.end : key.span.end,
},
);
return _c<NGMicrosyntaxLet>({
type: 'NGMicrosyntaxLet',
key: _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: key.source,
...key.span,
}),
value: !value
? null
: _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: value.source,
...value.span,
}),
start: sourceSpan.start,
end: value ? value.span.end : key.span.end,
});
} else {
const value = getAsVariableBindingValue(templateBinding);
return _c<NGMicrosyntaxAs>(
'NGMicrosyntaxAs',
{
key: _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: value!.source },
value!.span,
),
alias: _c<NGMicrosyntaxKey>(
'NGMicrosyntaxKey',
{ name: key.source },
key.span,
),
},
{ start: value!.span.start, end: key.span.end },
);
const value = getAsVariableBindingValue(templateBinding, context);
return _c<NGMicrosyntaxAs>({
type: 'NGMicrosyntaxAs',
key: _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: value!.source,
...value!.span,
}),
alias: _c<NGMicrosyntaxKey>({
type: 'NGMicrosyntaxKey',
name: key.source,
...key.span,
}),
start: value!.span.start,
end: key.span.end,
});
}
}
}

function _t<T extends NGNode>(n: ng.AST) {
return transformNode(n, context) as T;
function _t<T extends NGNode>(node: ng.AST) {
return transformNode(node, context) as T;
}

function _c<T extends NGNode>(
t: T['type'],
n: Partial<T>,
span: RawNGSpan,
stripSpaces = true,
properties: Partial<T> & { type: T['type'] } & RawNGSpan,
{ stripSpaces = true } = {},
) {
return {
type: t,
...transformSpan(span, context.text, { processSpan: stripSpaces }),
...n,
} as T;
return createNode<T>(context, properties, { stripSpaces });
}

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

function isExpressionBinding(
templateBinding: ng.TemplateBinding,
): templateBinding is ng.ExpressionBinding {
return templateBinding instanceof NGExpressionBinding;
}

function isVariableBinding(
templateBinding: ng.TemplateBinding,
): templateBinding is ng.VariableBinding {
return templateBinding instanceof NGVariableBinding;
}

function fixTemplateBindingSpan(templateBinding: ng.TemplateBinding) {
fixSpan(templateBinding.key.span);
fixSpan(templateBinding.key.span, context.text);
if (isVariableBinding(templateBinding) && templateBinding.value) {
fixSpan(templateBinding.value.span);
}
}

/**
* - "a" (start=0 end=1) -> (start=0 end=3)
* - '\'' (start=0 end=1) -> (start=0 end=4)
*/
function fixSpan(span: RawNGSpan) {
if (context.text[span.start] !== '"' && context.text[span.start] !== "'") {
return;
fixSpan(templateBinding.value.span, context.text);
}
const quote = context.text[span.start];
let hasBackSlash = false;
for (let i = span.start + 1; i < context.text.length; i++) {
switch (context.text[i]) {
case quote:
if (!hasBackSlash) {
span.end = i + 1;
return;
}
// fall through
default:
hasBackSlash = false;
break;
case '\\':
hasBackSlash = !hasBackSlash;
break;
}
}
}

/**
* - "as b" (value="NgEstreeParser" key="b") -> (value="$implicit" key="b")
*/
function getAsVariableBindingValue(
variableBinding: ng.VariableBinding,
): ng.VariableBinding['value'] {
if (
!variableBinding.value ||
variableBinding.value.source !== NG_PARSE_TEMPLATE_BINDINGS_FAKE_PREFIX
) {
return variableBinding.value;
}

const index = context.getCharacterIndex(
/\S/,
variableBinding.sourceSpan.start,
);
return {
source: '$implicit',
span: { start: index, end: index },
};
}
}

Expand Down
Loading

0 comments on commit dd407bd

Please sign in to comment.