Skip to content

Commit ac7e05c

Browse files
authored
Add printer.NodeFactory for printer/transformer-specific extensions (#815)
1 parent b3ae373 commit ac7e05c

12 files changed

+1017
-924
lines changed

internal/ast/ast.go

+394-386
Large diffs are not rendered by default.

internal/printer/emitcontext.go

+3-229
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,8 @@
11
package printer
22

33
import (
4-
"fmt"
54
"maps"
65
"slices"
7-
"strings"
86
"sync/atomic"
97

108
"github.com/microsoft/typescript-go/internal/ast"
@@ -16,7 +14,7 @@ import (
1614
//
1715
// NOTE: EmitContext is not guaranteed to be thread-safe.
1816
type EmitContext struct {
19-
Factory *ast.NodeFactory // Required. The NodeFactory to use to create new nodes
17+
Factory *NodeFactory // Required. The NodeFactory to use to create new nodes
2018
autoGenerate map[*ast.MemberName]*AutoGenerateInfo
2119
textSource map[*ast.StringLiteralNode]*ast.Node
2220
original map[*ast.Node]*ast.Node
@@ -37,11 +35,7 @@ type varScope struct {
3735

3836
func NewEmitContext() *EmitContext {
3937
c := &EmitContext{}
40-
c.Factory = ast.NewNodeFactory(ast.NodeFactoryHooks{
41-
OnCreate: c.onCreate,
42-
OnUpdate: c.onUpdate,
43-
OnClone: c.onClone,
44-
})
38+
c.Factory = NewNodeFactory(c)
4539
c.isCustomPrologue = c.isCustomPrologueWorker
4640
c.isHoistedFunction = c.isHoistedFunctionWorker
4741
c.isHoistedVariableStatement = c.isHoistedVariableStatementWorker
@@ -67,7 +61,7 @@ func (c *EmitContext) onClone(updated *ast.Node, original *ast.Node) {
6761

6862
// Creates a new NodeVisitor attached to this EmitContext
6963
func (c *EmitContext) NewNodeVisitor(visit func(node *ast.Node) *ast.Node) *ast.NodeVisitor {
70-
return ast.NewNodeVisitor(visit, c.Factory, ast.NodeVisitorHooks{
64+
return ast.NewNodeVisitor(visit, c.Factory.AsNodeFactory(), ast.NodeVisitorHooks{
7165
VisitParameters: c.VisitParameters,
7266
VisitFunctionBody: c.VisitFunctionBody,
7367
VisitIterationBody: c.VisitIterationBody,
@@ -338,149 +332,10 @@ func (c *EmitContext) isHoistedVariableStatementWorker(node *ast.Statement) bool
338332
core.Every(node.AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes, isHoistedVariable)
339333
}
340334

341-
// Ensures `"use strict"` is the first statement of a slice of statements.
342-
func (c *EmitContext) EnsureUseStrict(statements []*ast.Statement) []*ast.Statement {
343-
foundUseStrict := false
344-
for _, statement := range statements {
345-
if ast.IsPrologueDirective(statement) && statement.AsExpressionStatement().Expression.Text() == "use strict" {
346-
foundUseStrict = true
347-
} else {
348-
break
349-
}
350-
}
351-
if !foundUseStrict {
352-
useStrictPrologue := c.Factory.NewExpressionStatement(c.Factory.NewStringLiteral("use strict"))
353-
statements = append([]*ast.Statement{useStrictPrologue}, statements...)
354-
}
355-
return statements
356-
}
357-
358-
// Splits a slice of statements into two parts: standard prologue statements and the rest of the statements
359-
func (c *EmitContext) SplitStandardPrologue(source []*ast.Statement) (prologue []*ast.Statement, rest []*ast.Statement) {
360-
for i, statement := range source {
361-
if !ast.IsPrologueDirective(statement) {
362-
return source[:i], source[i:]
363-
}
364-
}
365-
return nil, source
366-
}
367-
368-
// Splits a slice of statements into two parts: custom prologue statements (e.g., with `EFCustomPrologue` set) and the rest of the statements
369-
func (c *EmitContext) SplitCustomPrologue(source []*ast.Statement) (prologue []*ast.Statement, rest []*ast.Statement) {
370-
for i, statement := range source {
371-
if ast.IsPrologueDirective(statement) || c.EmitFlags(statement)&EFCustomPrologue == 0 {
372-
return source[:i], source[i:]
373-
}
374-
}
375-
return nil, source
376-
}
377-
378335
//
379336
// Name Generation
380337
//
381338

382-
func (c *EmitContext) newGeneratedIdentifier(kind GeneratedIdentifierFlags, text string, node *ast.Node, options AutoGenerateOptions) *ast.IdentifierNode {
383-
id := AutoGenerateId(nextAutoGenerateId.Add(1))
384-
385-
if len(text) == 0 {
386-
switch {
387-
case node == nil:
388-
text = fmt.Sprintf("(auto@%d)", id)
389-
case ast.IsMemberName(node):
390-
text = node.Text()
391-
default:
392-
text = fmt.Sprintf("(generated@%v)", ast.GetNodeId(c.getNodeForGeneratedNameWorker(node, id)))
393-
}
394-
text = FormatGeneratedName(false /*privateName*/, options.Prefix, text, options.Suffix)
395-
}
396-
397-
name := c.Factory.NewIdentifier(text)
398-
autoGenerate := &AutoGenerateInfo{
399-
Id: id,
400-
Flags: kind | (options.Flags & ^GeneratedIdentifierFlagsKindMask),
401-
Prefix: options.Prefix,
402-
Suffix: options.Suffix,
403-
Node: node,
404-
}
405-
if c.autoGenerate == nil {
406-
c.autoGenerate = make(map[*ast.MemberName]*AutoGenerateInfo)
407-
}
408-
c.autoGenerate[name] = autoGenerate
409-
return name
410-
}
411-
412-
// Allocates a new temp variable name, but does not record it in the environment. It is recommended to pass this to either
413-
// `AddVariableDeclaration` or `AddLexicalDeclaration` to ensure it is properly tracked, if you are not otherwise handling
414-
// it yourself.
415-
func (c *EmitContext) NewTempVariable(options AutoGenerateOptions) *ast.IdentifierNode {
416-
return c.newGeneratedIdentifier(GeneratedIdentifierFlagsAuto, "", nil /*node*/, options)
417-
}
418-
419-
// Allocates a new loop variable name.
420-
func (c *EmitContext) NewLoopVariable(options AutoGenerateOptions) *ast.IdentifierNode {
421-
return c.newGeneratedIdentifier(GeneratedIdentifierFlagsLoop, "", nil /*node*/, options)
422-
}
423-
424-
// Allocates a new unique name based on the provided text.
425-
func (c *EmitContext) NewUniqueName(text string, options AutoGenerateOptions) *ast.IdentifierNode {
426-
return c.newGeneratedIdentifier(GeneratedIdentifierFlagsUnique, text, nil /*node*/, options)
427-
}
428-
429-
// Allocates a new unique name based on the provided node.
430-
func (c *EmitContext) NewGeneratedNameForNode(node *ast.Node, options AutoGenerateOptions) *ast.IdentifierNode {
431-
if len(options.Prefix) > 0 || len(options.Suffix) > 0 {
432-
options.Flags |= GeneratedIdentifierFlagsOptimistic
433-
}
434-
435-
return c.newGeneratedIdentifier(GeneratedIdentifierFlagsNode, "", node, options)
436-
}
437-
438-
func (c *EmitContext) newGeneratedPrivateIdentifier(kind GeneratedIdentifierFlags, text string, node *ast.Node, options AutoGenerateOptions) *ast.PrivateIdentifierNode {
439-
id := AutoGenerateId(nextAutoGenerateId.Add(1))
440-
441-
if len(text) == 0 {
442-
switch {
443-
case node == nil:
444-
text = fmt.Sprintf("(auto@%d)", id)
445-
case ast.IsMemberName(node):
446-
text = node.Text()
447-
default:
448-
text = fmt.Sprintf("(generated@%v)", ast.GetNodeId(c.getNodeForGeneratedNameWorker(node, id)))
449-
}
450-
text = FormatGeneratedName(true /*privateName*/, options.Prefix, text, options.Suffix)
451-
} else if !strings.HasPrefix(text, "#") {
452-
panic("First character of private identifier must be #: " + text)
453-
}
454-
455-
name := c.Factory.NewPrivateIdentifier(text)
456-
autoGenerate := &AutoGenerateInfo{
457-
Id: id,
458-
Flags: kind | (options.Flags &^ GeneratedIdentifierFlagsKindMask),
459-
Prefix: options.Prefix,
460-
Suffix: options.Suffix,
461-
Node: node,
462-
}
463-
if c.autoGenerate == nil {
464-
c.autoGenerate = make(map[*ast.MemberName]*AutoGenerateInfo)
465-
}
466-
c.autoGenerate[name] = autoGenerate
467-
return name
468-
}
469-
470-
// Allocates a new unique private name based on the provided text.
471-
func (c *EmitContext) NewUniquePrivateName(text string, options AutoGenerateOptions) *ast.PrivateIdentifierNode {
472-
return c.newGeneratedPrivateIdentifier(GeneratedIdentifierFlagsUnique, text, nil /*node*/, options)
473-
}
474-
475-
// Allocates a new unique private name based on the provided node.
476-
func (c *EmitContext) NewGeneratedPrivateNameForNode(node *ast.Node, options AutoGenerateOptions) *ast.PrivateIdentifierNode {
477-
if len(options.Prefix) > 0 || len(options.Suffix) > 0 {
478-
options.Flags |= GeneratedIdentifierFlagsOptimistic
479-
}
480-
481-
return c.newGeneratedPrivateIdentifier(GeneratedIdentifierFlagsNode, "", node, options)
482-
}
483-
484339
// Gets whether a given name has an associated AutoGenerateInfo entry.
485340
func (c *EmitContext) HasAutoGenerateInfo(node *ast.MemberName) bool {
486341
if node != nil {
@@ -544,87 +399,6 @@ type AutoGenerateInfo struct {
544399
Node *ast.Node // For a GeneratedIdentifierFlagsNode, the node from which to generate an identifier
545400
}
546401

547-
//
548-
// Factory Utilities
549-
//
550-
551-
// Allocates a new StringLiteral whose source text is derived from the provided node. This is often used to create a
552-
// string representation of an Identifier or NumericLiteral.
553-
func (c *EmitContext) NewStringLiteralFromNode(textSourceNode *ast.Node) *ast.Node {
554-
var text string
555-
if ast.IsMemberName(textSourceNode) || ast.IsJsxNamespacedName(textSourceNode) {
556-
text = textSourceNode.Text()
557-
}
558-
node := c.Factory.NewStringLiteral(text)
559-
if c.textSource == nil {
560-
c.textSource = make(map[*ast.StringLiteralNode]*ast.Node)
561-
}
562-
c.textSource[node] = textSourceNode
563-
return node
564-
}
565-
566-
// Allocates a new Identifier representing a reference to a helper function.
567-
func (c *EmitContext) NewUnscopedHelperName(name string) *ast.IdentifierNode {
568-
node := c.Factory.NewIdentifier(name)
569-
c.SetEmitFlags(node, EFHelperName)
570-
return node
571-
}
572-
573-
// Allocates a new Call expression to the `__importDefault` helper.
574-
func (c *EmitContext) NewImportDefaultHelper(expression *ast.Expression) *ast.Expression {
575-
c.RequestEmitHelper(importDefaultHelper)
576-
return c.Factory.NewCallExpression(
577-
c.NewUnscopedHelperName("__importDefault"),
578-
nil, /*questionDotToken*/
579-
nil, /*typeArguments*/
580-
c.Factory.NewNodeList([]*ast.Expression{expression}),
581-
ast.NodeFlagsNone,
582-
)
583-
}
584-
585-
// Allocates a new Call expression to the `__importStar` helper.
586-
func (c *EmitContext) NewImportStarHelper(expression *ast.Expression) *ast.Expression {
587-
c.RequestEmitHelper(importStarHelper)
588-
return c.Factory.NewCallExpression(
589-
c.NewUnscopedHelperName("__importStar"),
590-
nil, /*questionDotToken*/
591-
nil, /*typeArguments*/
592-
c.Factory.NewNodeList([]*ast.Expression{expression}),
593-
ast.NodeFlagsNone,
594-
)
595-
}
596-
597-
// Allocates a new Call expression to the `__exportStar` helper.
598-
func (c *EmitContext) NewExportStarHelper(moduleExpression *ast.Expression, exportsExpression *ast.Expression) *ast.Expression {
599-
c.RequestEmitHelper(exportStarHelper)
600-
c.RequestEmitHelper(createBindingHelper)
601-
return c.Factory.NewCallExpression(
602-
c.NewUnscopedHelperName("__exportStar"),
603-
nil, /*questionDotToken*/
604-
nil, /*typeArguments*/
605-
c.Factory.NewNodeList([]*ast.Expression{moduleExpression, exportsExpression}),
606-
ast.NodeFlagsNone,
607-
)
608-
}
609-
610-
// Allocates a new Call expression to the `__rewriteRelativeImportExtension` helper.
611-
func (c *EmitContext) NewRewriteRelativeImportExtensionsHelper(firstArgument *ast.Node, preserveJsx bool) *ast.Expression {
612-
c.RequestEmitHelper(rewriteRelativeImportExtensionsHelper)
613-
var arguments []*ast.Expression
614-
if preserveJsx {
615-
arguments = []*ast.Expression{firstArgument, c.Factory.NewToken(ast.KindTrueKeyword)}
616-
} else {
617-
arguments = []*ast.Expression{firstArgument}
618-
}
619-
return c.Factory.NewCallExpression(
620-
c.NewUnscopedHelperName("__rewriteRelativeImportExtension"),
621-
nil, /*questionDotToken*/
622-
nil, /*typeArguments*/
623-
c.Factory.NewNodeList(arguments),
624-
ast.NodeFlagsNone,
625-
)
626-
}
627-
628402
//
629403
// Original Node Tracking
630404
//

0 commit comments

Comments
 (0)