154 lines
5.4 KiB
Go
154 lines
5.4 KiB
Go
package tstransforms
|
|
|
|
import (
|
|
"github.com/microsoft/typescript-go/internal/ast"
|
|
"github.com/microsoft/typescript-go/internal/core"
|
|
"github.com/microsoft/typescript-go/internal/printer"
|
|
"github.com/microsoft/typescript-go/internal/transformers"
|
|
)
|
|
|
|
type ImportElisionTransformer struct {
|
|
transformers.Transformer
|
|
compilerOptions *core.CompilerOptions
|
|
currentSourceFile *ast.SourceFile
|
|
emitResolver printer.EmitResolver
|
|
}
|
|
|
|
func NewImportElisionTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
|
|
compilerOptions := opt.CompilerOptions
|
|
emitContext := opt.Context
|
|
if compilerOptions.VerbatimModuleSyntax.IsTrue() {
|
|
panic("ImportElisionTransformer should not be used with VerbatimModuleSyntax")
|
|
}
|
|
tx := &ImportElisionTransformer{compilerOptions: compilerOptions, emitResolver: opt.EmitResolver}
|
|
return tx.NewTransformer(tx.visit, emitContext)
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) visit(node *ast.Node) *ast.Node {
|
|
if ast.IsSourceFile(node) && tx.emitResolver != nil {
|
|
tx.emitResolver.MarkLinkedReferencesRecursively(tx.EmitContext().MostOriginal(node).AsSourceFile())
|
|
}
|
|
|
|
switch node.Kind {
|
|
case ast.KindImportEqualsDeclaration:
|
|
if ast.IsExternalModuleImportEqualsDeclaration(node) {
|
|
if !tx.shouldEmitAliasDeclaration(node) {
|
|
return nil
|
|
}
|
|
} else {
|
|
if !tx.shouldEmitImportEqualsDeclaration(node.AsImportEqualsDeclaration()) {
|
|
return nil
|
|
}
|
|
}
|
|
return tx.Visitor().VisitEachChild(node)
|
|
case ast.KindImportDeclaration:
|
|
n := node.AsImportDeclaration()
|
|
// Do not elide a side-effect only import declaration.
|
|
// import "foo";
|
|
if n.ImportClause != nil {
|
|
importClause := tx.Visitor().VisitNode(n.ImportClause)
|
|
if importClause == nil {
|
|
return nil
|
|
}
|
|
return tx.Factory().UpdateImportDeclaration(n, n.Modifiers(), importClause, n.ModuleSpecifier, tx.Visitor().VisitNode(n.Attributes))
|
|
}
|
|
return tx.Visitor().VisitEachChild(node)
|
|
case ast.KindImportClause:
|
|
n := node.AsImportClause()
|
|
name := core.IfElse(tx.shouldEmitAliasDeclaration(node), n.Name(), nil)
|
|
namedBindings := tx.Visitor().VisitNode(n.NamedBindings)
|
|
if name == nil && namedBindings == nil {
|
|
// all import bindings were elided
|
|
return nil
|
|
}
|
|
return tx.Factory().UpdateImportClause(n, n.PhaseModifier, name, namedBindings)
|
|
case ast.KindNamespaceImport:
|
|
if !tx.shouldEmitAliasDeclaration(node) {
|
|
// elide unused imports
|
|
return nil
|
|
}
|
|
return node
|
|
case ast.KindNamedImports:
|
|
n := node.AsNamedImports()
|
|
elements := tx.Visitor().VisitNodes(n.Elements)
|
|
if len(elements.Nodes) == 0 {
|
|
// all import specifiers were elided
|
|
return nil
|
|
}
|
|
return tx.Factory().UpdateNamedImports(n, elements)
|
|
case ast.KindImportSpecifier:
|
|
if !tx.shouldEmitAliasDeclaration(node) {
|
|
// elide type-only or unused imports
|
|
return nil
|
|
}
|
|
return node
|
|
case ast.KindExportAssignment:
|
|
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && !tx.isValueAliasDeclaration(node) {
|
|
// elide unused import
|
|
return nil
|
|
}
|
|
return tx.Visitor().VisitEachChild(node)
|
|
case ast.KindExportDeclaration:
|
|
n := node.AsExportDeclaration()
|
|
var exportClause *ast.Node
|
|
if n.ExportClause != nil {
|
|
exportClause = tx.Visitor().VisitNode(n.ExportClause)
|
|
if exportClause == nil {
|
|
// all export bindings were elided
|
|
return nil
|
|
}
|
|
}
|
|
return tx.Factory().UpdateExportDeclaration(n, nil /*modifiers*/, false /*isTypeOnly*/, exportClause, tx.Visitor().VisitNode(n.ModuleSpecifier), tx.Visitor().VisitNode(n.Attributes))
|
|
case ast.KindNamedExports:
|
|
n := node.AsNamedExports()
|
|
elements := tx.Visitor().VisitNodes(n.Elements)
|
|
if len(elements.Nodes) == 0 {
|
|
// all export specifiers were elided
|
|
return nil
|
|
}
|
|
return tx.Factory().UpdateNamedExports(n, elements)
|
|
case ast.KindExportSpecifier:
|
|
if !tx.isValueAliasDeclaration(node) {
|
|
// elide unused export
|
|
return nil
|
|
}
|
|
return node
|
|
case ast.KindSourceFile:
|
|
savedCurrentSourceFile := tx.currentSourceFile
|
|
tx.currentSourceFile = node.AsSourceFile()
|
|
node = tx.Visitor().VisitEachChild(node)
|
|
tx.currentSourceFile = savedCurrentSourceFile
|
|
return node
|
|
case ast.KindModuleDeclaration, ast.KindModuleBlock:
|
|
return tx.Visitor().VisitEachChild(node)
|
|
default:
|
|
return node
|
|
}
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) shouldEmitAliasDeclaration(node *ast.Node) bool {
|
|
return ast.IsInJSFile(node) || tx.isReferencedAliasDeclaration(node)
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) shouldEmitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) bool {
|
|
// preserve old compiler's behavior: emit import declaration (even if we do not consider them referenced) when
|
|
// - current file is not external module
|
|
// - import declaration is top level and target is value imported by entity name
|
|
return tx.shouldEmitAliasDeclaration(node.AsNode()) || (!ast.IsExternalModule(tx.currentSourceFile) && tx.isTopLevelValueImportEqualsWithEntityName(node.AsNode()))
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) isReferencedAliasDeclaration(node *ast.Node) bool {
|
|
node = tx.EmitContext().ParseNode(node)
|
|
return node == nil || tx.emitResolver.IsReferencedAliasDeclaration(node)
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) isValueAliasDeclaration(node *ast.Node) bool {
|
|
node = tx.EmitContext().ParseNode(node)
|
|
return node == nil || tx.emitResolver.IsValueAliasDeclaration(node)
|
|
}
|
|
|
|
func (tx *ImportElisionTransformer) isTopLevelValueImportEqualsWithEntityName(node *ast.Node) bool {
|
|
node = tx.EmitContext().ParseNode(node)
|
|
return node != nil && tx.emitResolver.IsTopLevelValueImportEqualsWithEntityName(node)
|
|
}
|