From 2b006ae487d0fafa9dae47cd4cd82d7f011e8b77 Mon Sep 17 00:00:00 2001 From: fisker Date: Wed, 17 Dec 2025 04:05:20 +0800 Subject: [PATCH 1/4] Simplify transform --- prettier.config.js | 5 + src/source.ts | 20 +- src/transform-node.ts | 385 +++++++++++------------------- src/transform-template-binding.ts | 160 +++++++------ tests/transform.test.ts | 1 - 5 files changed, 260 insertions(+), 311 deletions(-) diff --git a/prettier.config.js b/prettier.config.js index a1a872dc..73c95b53 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -1,4 +1,9 @@ +/** + * @see https://prettier.io/docs/configuration + * @type {import("prettier").Config} + */ export default { singleQuote: true, trailingComma: 'all', + objectWrap: 'collapse', }; diff --git a/src/source.ts b/src/source.ts index c79701ba..c314e541 100644 --- a/src/source.ts +++ b/src/source.ts @@ -1,3 +1,4 @@ +import * as angular from '@angular/compiler'; import type * as babel from '@babel/types'; import type { LocationInformation, NGNode, RawNGSpan } from './types.ts'; @@ -27,11 +28,26 @@ export class Source { } createNode( - properties: Partial & { type: T['type'] } & RawNGSpan, + properties: Partial & { type: T['type'] }, + location: angular.AST | RawNGSpan | [number, number], ) { + let start: number; + let end: number; + let range: [number, number]; + if (Array.isArray(location)) { + range = location; + [start, end] = location; + } else { + ({ start, end } = + location instanceof angular.AST ? location.sourceSpan : location); + range = [start, end]; + } + const node = { + start, + end, + range, ...properties, - range: [properties.start, properties.end], } as T & LocationInformation; switch (node.type) { diff --git a/src/transform-node.ts b/src/transform-node.ts index 26ad0493..b13ccd9c 100644 --- a/src/transform-node.ts +++ b/src/transform-node.ts @@ -45,10 +45,11 @@ class Transformer extends Source { } #create( - properties: Partial & { type: T['type'] } & RawNGSpan, + properties: Partial & { type: T['type'] }, + location: angular.AST | RawNGSpan | [number, number], ancestors: angular.AST[], ) { - const node = super.createNode(properties); + const node = super.createNode(properties, location); if (ancestors[0] instanceof angular.ParenthesizedExpression) { node.extra = { @@ -62,10 +63,11 @@ class Transformer extends Source { #transform(node: angular.AST, options: NodeTransformOptions): NGNode { const ancestors = options.ancestors; - const childTransformOptions = { - ...options, - ancestors: [node, ...ancestors], - }; + const transformChild = (child: angular.AST) => + this.transform(child, { ancestors: [node, ...ancestors] }); + const createNode = ( + properties: Partial & { type: T['type'] }, + ) => this.#create(properties, node, ancestors); if (node instanceof angular.Interpolation) { const { expressions } = node; @@ -75,112 +77,81 @@ class Transformer extends Source { throw new Error("Unexpected 'Interpolation'"); } - return this.transform(expressions[0], childTransformOptions); + return transformChild(expressions[0]); } if (node instanceof angular.Unary) { - return this.#create( - { - type: 'UnaryExpression', - prefix: true, - argument: this.transform(node.expr), - operator: node.operator as '-' | '+', - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'UnaryExpression', + prefix: true, + argument: transformChild(node.expr), + operator: node.operator as '-' | '+', + }); } if (node instanceof angular.Binary) { const { operation: operator } = node; const [left, right] = [node.left, node.right].map((node) => - this.transform(node, childTransformOptions), + transformChild(node), ); if (operator === '&&' || operator === '||' || operator === '??') { - return this.#create( - { - type: 'LogicalExpression', - operator: operator as babel.LogicalExpression['operator'], - left, - right, - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'LogicalExpression', + operator: operator as babel.LogicalExpression['operator'], + left, + right, + }); } if (angular.Binary.isAssignmentOperation(operator)) { - return this.#create( - { - type: 'AssignmentExpression', - left: left as babel.MemberExpression, - right, - operator: operator as babel.AssignmentExpression['operator'], - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'AssignmentExpression', + left: left as babel.MemberExpression, + right, + operator: operator as babel.AssignmentExpression['operator'], + }); } - return this.#create( - { - left, - right, - type: 'BinaryExpression', - operator: operator as babel.BinaryExpression['operator'], - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + left, + right, + type: 'BinaryExpression', + operator: operator as babel.BinaryExpression['operator'], + }); } if (node instanceof angular.BindingPipe) { const { name } = node; - const left = this.transform( - node.exp, - childTransformOptions, - ); + const left = transformChild(node.exp); const leftEnd = node.exp.sourceSpan.end; const rightStart = super.getCharacterIndex( /\S/, super.getCharacterIndex('|', leftEnd) + 1, ); const right = this.#create( - { - type: 'Identifier', - name, - start: rightStart, - end: rightStart + name.length, - }, + { type: 'Identifier', name }, + [rightStart, rightStart + name.length], ancestors, ); const arguments_ = node.args.map((node) => - this.transform(node, childTransformOptions), - ); - return this.#create( - { - type: 'NGPipeExpression', - left, - right, - arguments: arguments_, - ...node.sourceSpan, - }, - ancestors, + transformChild(node), ); + return createNode({ + type: 'NGPipeExpression', + left, + right, + arguments: arguments_, + }); } if (node instanceof angular.Chain) { - return this.#create( - { - type: 'NGChainedExpression', - expressions: node.expressions.map((node) => - this.transform(node, childTransformOptions), - ), - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'NGChainedExpression', + expressions: node.expressions.map((node) => + transformChild(node), + ), + }); } if (node instanceof angular.Conditional) { @@ -188,47 +159,31 @@ class Transformer extends Source { node.condition, node.trueExp, node.falseExp, - ].map((node) => - this.transform(node, childTransformOptions), - ); + ].map((node) => transformChild(node)); - return this.#create( - { - type: 'ConditionalExpression', - test, - consequent, - alternate, - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'ConditionalExpression', + test, + consequent, + alternate, + }); } if (node instanceof angular.EmptyExpr) { - return this.#create( - { type: 'NGEmptyExpression', ...node.sourceSpan }, - ancestors, - ); + return createNode({ type: 'NGEmptyExpression' }); } if (node instanceof angular.ImplicitReceiver) { - return this.#create( - { type: 'ThisExpression', ...node.sourceSpan }, - ancestors, - ); + return createNode({ type: 'ThisExpression' }); } if (node instanceof angular.LiteralArray) { - return this.#create( - { - type: 'ArrayExpression', - elements: node.expressions.map((node) => - this.transform(node, childTransformOptions), - ), - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'ArrayExpression', + elements: node.expressions.map((node) => + transformChild(node), + ), + }); } if (node instanceof angular.LiteralMap) { @@ -251,29 +206,25 @@ class Transformer extends Source { /\S/, super.getCharacterLastIndex(':', valueStart - 1) - 1, ) + 1; - const keySpan = { start: keyStart, end: keyEnd }; const tKey = quoted ? this.#create( { type: 'StringLiteral', value: key, - ...keySpan, }, + [keyStart, keyEnd], [], ) : this.#create( { type: 'Identifier', name: key, - ...keySpan, }, + [keyStart, keyEnd], [], ); const shorthand = tKey.end < tKey.start || keyStart === valueStart; - const value = this.transform( - values[index], - childTransformOptions, - ); + const value = transformChild(values[index]); return this.#create( { @@ -282,50 +233,42 @@ class Transformer extends Source { value, shorthand, computed: false, - start: tKey.start, - end: valueEnd, }, + [tKey.start, valueEnd], [], ); }); - return this.#create( - { - type: 'ObjectExpression', - properties: tProperties, - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'ObjectExpression', + properties: tProperties, + }); } if (node instanceof angular.LiteralPrimitive) { const { value } = node; switch (typeof value) { case 'boolean': - return this.#create( - { type: 'BooleanLiteral', value, ...node.sourceSpan }, - ancestors, - ); + return createNode({ + type: 'BooleanLiteral', + value, + }); case 'number': - return this.#create( - { type: 'NumericLiteral', value, ...node.sourceSpan }, - ancestors, - ); + return createNode({ + type: 'NumericLiteral', + value, + }); case 'object': - return this.#create( - { type: 'NullLiteral', ...node.sourceSpan }, - ancestors, - ); + return createNode({ type: 'NullLiteral' }); case 'string': - return this.#create( - { type: 'StringLiteral', value, ...node.sourceSpan }, - ancestors, - ); + return createNode({ + type: 'StringLiteral', + value, + }); case 'undefined': - return this.#create( - { type: 'Identifier', name: 'undefined', ...node.sourceSpan }, - ancestors, - ); + return createNode({ + type: 'Identifier', + name: 'undefined', + }); /* c8 ignore next 4 */ default: throw new Error( @@ -335,52 +278,39 @@ class Transformer extends Source { } if (node instanceof angular.RegularExpressionLiteral) { - return this.#create( - { - type: 'RegExpLiteral', - pattern: node.body, - flags: node.flags ?? '', - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'RegExpLiteral', + pattern: node.body, + flags: node.flags ?? '', + }); } if (node instanceof angular.Call || node instanceof angular.SafeCall) { const arguments_ = node.args.map((node) => - this.transform(node, childTransformOptions), + transformChild(node), ); - const callee = this.transform(node.receiver); + const callee = transformChild(node.receiver); const isOptionalReceiver = isOptionalObjectOrCallee(callee); const isOptional = node instanceof angular.SafeCall; const nodeType = isOptional || isOptionalReceiver ? 'OptionalCallExpression' : 'CallExpression'; - return this.#create( - { - type: nodeType, - callee, - arguments: arguments_, - ...(nodeType === 'OptionalCallExpression' - ? { optional: isOptional } - : undefined), - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: nodeType, + callee, + arguments: arguments_, + ...(nodeType === 'OptionalCallExpression' + ? { optional: isOptional } + : undefined), + }); } if (node instanceof angular.NonNullAssert) { - const expression = this.transform(node.expression); - return this.#create( - { - type: 'TSNonNullExpression', - expression: expression, - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'TSNonNullExpression', + expression: transformChild(node.expression), + }); } if ( @@ -428,9 +358,8 @@ class Transformer extends Source { prefix: true, operator, argument: expression, - start, - end: node.sourceSpan.end, }, + [start, node.sourceSpan.end], ancestors, ); } @@ -455,7 +384,7 @@ class Transformer extends Source { let property; if (isComputed) { isImplicitThis = node.sourceSpan.start === node.key.sourceSpan.start; - property = this.transform(node.key); + property = transformChild(node.key); } else { const { name, nameSpan } = node; @@ -464,8 +393,8 @@ class Transformer extends Source { { type: 'Identifier', name, - ...node.nameSpan, }, + node.nameSpan, isImplicitThis ? ancestors : [], ); } @@ -477,72 +406,49 @@ class Transformer extends Source { const object = this.transform(receiver); const isOptionalObject = isOptionalObjectOrCallee(object); - const commonProps = { - property, - object, - ...node.sourceSpan, - }; - if (isOptional || isOptionalObject) { - return this.#create( - { - type: 'OptionalMemberExpression', - optional: isOptional || !isOptionalObject, - computed: isComputed, - ...commonProps, - }, - ancestors, - ); + return createNode({ + type: 'OptionalMemberExpression', + optional: isOptional || !isOptionalObject, + computed: isComputed, + property, + object, + }); } if (isComputed) { - return this.#create( - { - type: 'MemberExpression', - ...commonProps, - computed: true, - }, - ancestors, - ); + return createNode({ + type: 'MemberExpression', + property, + object, + computed: true, + }); } - return this.#create( - { - type: 'MemberExpression', - ...commonProps, - computed: false, - property: property as babel.MemberExpressionNonComputed['property'], - }, - ancestors, - ); + return createNode({ + type: 'MemberExpression', + object, + property: property as babel.MemberExpressionNonComputed['property'], + computed: false, + }); } if (node instanceof angular.TaggedTemplateLiteral) { - return this.#create( - { - type: 'TaggedTemplateExpression', - tag: this.transform(node.tag), - quasi: this.transform(node.template), - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'TaggedTemplateExpression', + tag: transformChild(node.tag), + quasi: transformChild(node.template), + }); } if (node instanceof angular.TemplateLiteral) { - return this.#create( - { - type: 'TemplateLiteral', - quasis: node.elements.map((element) => - this.transform(element, childTransformOptions), - ), - expressions: node.expressions.map((expression) => - this.transform(expression, childTransformOptions), - ), - ...node.sourceSpan, - }, - ancestors, - ); + return createNode({ + type: 'TemplateLiteral', + quasis: node.elements.map((element) => transformChild(element)), + expressions: node.expressions.map((expression) => + transformChild(expression), + ), + }); } if (node instanceof angular.TemplateLiteralElement) { @@ -563,16 +469,15 @@ class Transformer extends Source { cooked: node.text, raw, }, - start: start, - end: end, tail: isLast, }, + [start, end], ancestors, ); } if (node instanceof angular.ParenthesizedExpression) { - return this.transform(node.expression, childTransformOptions); + return transformChild(node.expression); } /* c8 ignore next @preserve */ diff --git a/src/transform-template-binding.ts b/src/transform-template-binding.ts index 19ce3200..53ee018c 100644 --- a/src/transform-template-binding.ts +++ b/src/transform-template-binding.ts @@ -54,9 +54,10 @@ class TemplateBindingTransformer extends NodeTransformer { } #create( - properties: Partial & { type: T['type'] } & RawNGSpan, + properties: Partial & { type: T['type'] }, + location: angular.AST | RawNGSpan | [number, number], ) { - return this.createNode(properties); + return this.createNode(properties, location); } #transform(node: angular.AST) { @@ -150,11 +151,13 @@ class TemplateBindingTransformer extends NodeTransformer { templateBinding.value && templateBinding.value.source === lastTemplateBinding.key.source ) { - const alias = this.#create({ - type: 'NGMicrosyntaxKey', - name: templateBinding.key.source, - ...templateBinding.key.span, - }); + const alias = this.#create( + { + type: 'NGMicrosyntaxKey', + name: templateBinding.key.source, + }, + templateBinding.key.span, + ); const updateSpanEnd = (node: T, end: number): T => ({ ...node, ...this.transformSpan({ start: node.start!, end }), @@ -184,13 +187,15 @@ class TemplateBindingTransformer extends NodeTransformer { lastTemplateBinding = templateBinding; } - return this.#create({ - type: 'NGMicrosyntax', - body, - ...(body.length === 0 + return this.#create( + { + 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 }, + ); } #transformTemplateBinding( @@ -200,35 +205,44 @@ class TemplateBindingTransformer extends NodeTransformer { if (isExpressionBinding(templateBinding)) { const { key, value } = templateBinding; if (!value) { - return this.#create({ - type: 'NGMicrosyntaxKey', - name: this.#removePrefix(key.source), - ...key.span, - }); - } else if (index === 0) { - return this.#create({ - type: 'NGMicrosyntaxExpression', - expression: this.#transform(value.ast), - alias: null, - ...value.sourceSpan, - }); - } else { - return this.#create({ - type: 'NGMicrosyntaxKeyedExpression', - key: this.#create({ + return this.#create( + { type: 'NGMicrosyntaxKey', name: this.#removePrefix(key.source), - ...key.span, - }), - expression: this.#create({ + }, + key.span, + ); + } else if (index === 0) { + return this.#create( + { type: 'NGMicrosyntaxExpression', expression: this.#transform(value.ast), alias: null, - ...value.sourceSpan, - }), - start: key.span.start, - end: value.sourceSpan.end, - }); + }, + value, + ); + } else { + return this.#create( + { + type: 'NGMicrosyntaxKeyedExpression', + key: this.#create( + { + type: 'NGMicrosyntaxKey', + name: this.#removePrefix(key.source), + }, + key.span, + ), + expression: this.#create( + { + type: 'NGMicrosyntaxExpression', + expression: this.#transform(value.ast), + alias: null, + }, + value, + ), + }, + [key.span.start, value.sourceSpan.end], + ); } } else { const { key, sourceSpan } = templateBinding; @@ -237,40 +251,50 @@ class TemplateBindingTransformer extends NodeTransformer { ); if (startsWithLet) { const { value } = templateBinding; - return this.#create({ - type: 'NGMicrosyntaxLet', - key: this.#create({ - type: 'NGMicrosyntaxKey', - name: key.source, - ...key.span, - }), - value: !value - ? null - : this.#create({ + return this.#create( + { + type: 'NGMicrosyntaxLet', + key: this.#create( + { type: 'NGMicrosyntaxKey', - name: value.source, - ...value.span, - }), - start: sourceSpan.start, - end: value ? value.span.end : key.span.end, - }); + name: key.source, + }, + key.span, + ), + value: !value + ? null + : this.#create( + { + type: 'NGMicrosyntaxKey', + name: value.source, + }, + value.span, + ), + }, + [sourceSpan.start, value ? value.span.end : key.span.end], + ); } else { const value = this.#getAsVariableBindingValue(templateBinding); - return this.#create({ - type: 'NGMicrosyntaxAs', - key: this.#create({ - type: 'NGMicrosyntaxKey', - name: value!.source, - ...value!.span, - }), - alias: this.#create({ - type: 'NGMicrosyntaxKey', - name: key.source, - ...key.span, - }), - start: value!.span.start, - end: key.span.end, - }); + return this.#create( + { + type: 'NGMicrosyntaxAs', + key: this.#create( + { + type: 'NGMicrosyntaxKey', + name: value!.source, + }, + value!.span, + ), + alias: this.#create( + { + type: 'NGMicrosyntaxKey', + name: key.source, + }, + key.span, + ), + }, + [value!.span.start, key.span.end], + ); } } } diff --git a/tests/transform.test.ts b/tests/transform.test.ts index 2073e85c..8fe7ab74 100644 --- a/tests/transform.test.ts +++ b/tests/transform.test.ts @@ -33,7 +33,6 @@ describe.each` ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) && ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) || ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) ?? ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} - ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a . b = ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false} ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a = ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false} ${'Binary'} | ${'AssignmentExpression'} | ${' a [ b ] = 1 '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a ??= ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false} From 49f94a791a53957f25a41db7fa6bc200842b9801 Mon Sep 17 00:00:00 2001 From: fisker Date: Wed, 17 Dec 2025 04:14:59 +0800 Subject: [PATCH 2/4] `transformChildren` --- src/transform-node.ts | 80 +++++++++++++++++-------------------------- 1 file changed, 32 insertions(+), 48 deletions(-) diff --git a/src/transform-node.ts b/src/transform-node.ts index b13ccd9c..1a3d447f 100644 --- a/src/transform-node.ts +++ b/src/transform-node.ts @@ -65,9 +65,13 @@ class Transformer extends Source { const ancestors = options.ancestors; const transformChild = (child: angular.AST) => this.transform(child, { ancestors: [node, ...ancestors] }); + const transformChildren = (children: angular.AST[]) => + children.map((child) => transformChild(child)); const createNode = ( properties: Partial & { type: T['type'] }, - ) => this.#create(properties, node, ancestors); + location: angular.AST | RawNGSpan | [number, number] = node, + ancestorsToCreate: angular.AST[] = ancestors, + ) => this.#create(properties, location, ancestorsToCreate); if (node instanceof angular.Interpolation) { const { expressions } = node; @@ -91,9 +95,10 @@ class Transformer extends Source { if (node instanceof angular.Binary) { const { operation: operator } = node; - const [left, right] = [node.left, node.right].map((node) => - transformChild(node), - ); + const [left, right] = transformChildren([ + node.left, + node.right, + ]); if (operator === '&&' || operator === '||' || operator === '??') { return createNode({ @@ -129,14 +134,11 @@ class Transformer extends Source { /\S/, super.getCharacterIndex('|', leftEnd) + 1, ); - const right = this.#create( - { type: 'Identifier', name }, - [rightStart, rightStart + name.length], - ancestors, - ); - const arguments_ = node.args.map((node) => - transformChild(node), - ); + const right = createNode({ type: 'Identifier', name }, [ + rightStart, + rightStart + name.length, + ]); + const arguments_ = transformChildren(node.args); return createNode({ type: 'NGPipeExpression', left, @@ -148,18 +150,14 @@ class Transformer extends Source { if (node instanceof angular.Chain) { return createNode({ type: 'NGChainedExpression', - expressions: node.expressions.map((node) => - transformChild(node), - ), + expressions: transformChildren(node.expressions), }); } if (node instanceof angular.Conditional) { - const [test, consequent, alternate] = [ - node.condition, - node.trueExp, - node.falseExp, - ].map((node) => transformChild(node)); + const [test, consequent, alternate] = transformChildren( + [node.condition, node.trueExp, node.falseExp], + ); return createNode({ type: 'ConditionalExpression', @@ -180,9 +178,7 @@ class Transformer extends Source { if (node instanceof angular.LiteralArray) { return createNode({ type: 'ArrayExpression', - elements: node.expressions.map((node) => - transformChild(node), - ), + elements: transformChildren(node.expressions), }); } @@ -207,7 +203,7 @@ class Transformer extends Source { super.getCharacterLastIndex(':', valueStart - 1) - 1, ) + 1; const tKey = quoted - ? this.#create( + ? createNode( { type: 'StringLiteral', value: key, @@ -215,7 +211,7 @@ class Transformer extends Source { [keyStart, keyEnd], [], ) - : this.#create( + : createNode( { type: 'Identifier', name: key, @@ -226,7 +222,7 @@ class Transformer extends Source { const shorthand = tKey.end < tKey.start || keyStart === valueStart; const value = transformChild(values[index]); - return this.#create( + return createNode( { type: 'ObjectProperty', key: tKey, @@ -286,9 +282,7 @@ class Transformer extends Source { } if (node instanceof angular.Call || node instanceof angular.SafeCall) { - const arguments_ = node.args.map((node) => - transformChild(node), - ); + const arguments_ = transformChildren(node.args); const callee = transformChild(node.receiver); const isOptionalReceiver = isOptionalObjectOrCallee(callee); const isOptional = node instanceof angular.SafeCall; @@ -350,9 +344,9 @@ class Transformer extends Source { start = index; } - const expression = this.transform(node.expression); + const expression = transformChild(node.expression); - return this.#create( + return createNode( { type: 'UnaryExpression', prefix: true, @@ -360,7 +354,6 @@ class Transformer extends Source { argument: expression, }, [start, node.sourceSpan.end], - ancestors, ); } @@ -389,11 +382,8 @@ class Transformer extends Source { const { name, nameSpan } = node; isImplicitThis = node.sourceSpan.start === nameSpan.start; - property = this.#create( - { - type: 'Identifier', - name, - }, + property = createNode( + { type: 'Identifier', name }, node.nameSpan, isImplicitThis ? ancestors : [], ); @@ -403,7 +393,7 @@ class Transformer extends Source { return property; } - const object = this.transform(receiver); + const object = transformChild(receiver); const isOptionalObject = isOptionalObjectOrCallee(object); if (isOptional || isOptionalObject) { @@ -444,10 +434,8 @@ class Transformer extends Source { if (node instanceof angular.TemplateLiteral) { return createNode({ type: 'TemplateLiteral', - quasis: node.elements.map((element) => transformChild(element)), - expressions: node.expressions.map((expression) => - transformChild(expression), - ), + quasis: transformChildren(node.elements), + expressions: transformChildren(node.expressions), }); } @@ -462,17 +450,13 @@ class Transformer extends Source { const start = node.sourceSpan.start + (isFirst ? 1 : 0); const raw = this.text.slice(start, end); - return this.#create( + return createNode( { type: 'TemplateElement', - value: { - cooked: node.text, - raw, - }, + value: { cooked: node.text, raw }, tail: isLast, }, [start, end], - ancestors, ); } From a888caaf3525c3f4c7454862def25157128f32f4 Mon Sep 17 00:00:00 2001 From: fisker Date: Wed, 17 Dec 2025 04:24:58 +0800 Subject: [PATCH 3/4] Minor tweak --- eslint.config.js | 2 +- prettier.config.js | 1 - src/angular-parser.ts | 5 +--- src/transform-node.ts | 10 ++------ src/transform-template-binding.ts | 40 +++++++------------------------ 5 files changed, 12 insertions(+), 46 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index ab394969..736cca87 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -6,7 +6,7 @@ import globals from 'globals'; import tseslint from 'typescript-eslint'; export default tseslint.config( - { ignores: ['coverage/', 'lib/', '**/.yarn/**', '**/.pnp.*'] }, + { ignores: ['coverage/', 'lib/', '**/.yarn/**', '**/.pnp.*', 'dist*/'] }, eslintPluginJs.configs.recommended, ...tseslint.configs.recommended, eslintConfigPrettier, diff --git a/prettier.config.js b/prettier.config.js index 73c95b53..8dfde402 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -5,5 +5,4 @@ export default { singleQuote: true, trailingComma: 'all', - objectWrap: 'collapse', }; diff --git a/src/angular-parser.ts b/src/angular-parser.ts index 612ced36..2705f9d3 100644 --- a/src/angular-parser.ts +++ b/src/angular-parser.ts @@ -38,10 +38,7 @@ function extractComments(text: string, shouldExtractComment: boolean) { }), }; - return { - text: text.slice(0, commentStart), - comments: [comment], - }; + return { text: text.slice(0, commentStart), comments: [comment] }; } function createAngularParseFunction< diff --git a/src/transform-node.ts b/src/transform-node.ts index 1a3d447f..a0169ba3 100644 --- a/src/transform-node.ts +++ b/src/transform-node.ts @@ -204,18 +204,12 @@ class Transformer extends Source { ) + 1; const tKey = quoted ? createNode( - { - type: 'StringLiteral', - value: key, - }, + { type: 'StringLiteral', value: key }, [keyStart, keyEnd], [], ) : createNode( - { - type: 'Identifier', - name: key, - }, + { type: 'Identifier', name: key }, [keyStart, keyEnd], [], ); diff --git a/src/transform-template-binding.ts b/src/transform-template-binding.ts index 53ee018c..8a9c6004 100644 --- a/src/transform-template-binding.ts +++ b/src/transform-template-binding.ts @@ -152,10 +152,7 @@ class TemplateBindingTransformer extends NodeTransformer { templateBinding.value.source === lastTemplateBinding.key.source ) { const alias = this.#create( - { - type: 'NGMicrosyntaxKey', - name: templateBinding.key.source, - }, + { type: 'NGMicrosyntaxKey', name: templateBinding.key.source }, templateBinding.key.span, ); const updateSpanEnd = (node: T, end: number): T => ({ @@ -164,10 +161,7 @@ class TemplateBindingTransformer extends NodeTransformer { }); const updateExpressionAlias = ( expression: NGMicrosyntaxExpression, - ) => ({ - ...updateSpanEnd(expression, alias.end), - alias, - }); + ) => ({ ...updateSpanEnd(expression, alias.end), alias }); const lastNode = body.pop()!; @@ -188,10 +182,7 @@ class TemplateBindingTransformer extends NodeTransformer { } return this.#create( - { - type: 'NGMicrosyntax', - body, - }, + { type: 'NGMicrosyntax', body }, body.length === 0 ? rawTemplateBindings[0].sourceSpan : { start: body[0].start, end: body.at(-1)!.end }, @@ -206,10 +197,7 @@ class TemplateBindingTransformer extends NodeTransformer { const { key, value } = templateBinding; if (!value) { return this.#create( - { - type: 'NGMicrosyntaxKey', - name: this.#removePrefix(key.source), - }, + { type: 'NGMicrosyntaxKey', name: this.#removePrefix(key.source) }, key.span, ); } else if (index === 0) { @@ -255,19 +243,13 @@ class TemplateBindingTransformer extends NodeTransformer { { type: 'NGMicrosyntaxLet', key: this.#create( - { - type: 'NGMicrosyntaxKey', - name: key.source, - }, + { type: 'NGMicrosyntaxKey', name: key.source }, key.span, ), value: !value ? null : this.#create( - { - type: 'NGMicrosyntaxKey', - name: value.source, - }, + { type: 'NGMicrosyntaxKey', name: value.source }, value.span, ), }, @@ -279,17 +261,11 @@ class TemplateBindingTransformer extends NodeTransformer { { type: 'NGMicrosyntaxAs', key: this.#create( - { - type: 'NGMicrosyntaxKey', - name: value!.source, - }, + { type: 'NGMicrosyntaxKey', name: value!.source }, value!.span, ), alias: this.#create( - { - type: 'NGMicrosyntaxKey', - name: key.source, - }, + { type: 'NGMicrosyntaxKey', name: key.source }, key.span, ), }, From 4af45465d02da34d2addad818846e91349316949 Mon Sep 17 00:00:00 2001 From: fisker Date: Wed, 17 Dec 2025 04:25:46 +0800 Subject: [PATCH 4/4] Restore test --- tests/transform.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/transform.test.ts b/tests/transform.test.ts index 8fe7ab74..2073e85c 100644 --- a/tests/transform.test.ts +++ b/tests/transform.test.ts @@ -33,6 +33,7 @@ describe.each` ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) && ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) || ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'LogicalExpression'} | ${' ( ( ( ( a ) ) ?? ( ( b ) ) ) ) '} | ${true} | ${true} | ${true} | ${true} + ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a . b = ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false} ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a = ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false} ${'Binary'} | ${'AssignmentExpression'} | ${' a [ b ] = 1 '} | ${true} | ${true} | ${true} | ${true} ${'Binary'} | ${'AssignmentExpression'} | ${' ( ( a ??= ( ( 1 ) ) ) ) '} | ${true} | ${false} | ${false} | ${false}