vendor tsgo
This commit is contained in:
153
tools/tsgo/internal/transformers/tstransforms/importelision.go
Normal file
153
tools/tsgo/internal/transformers/tstransforms/importelision.go
Normal file
@@ -0,0 +1,153 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
package tstransforms_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/binder"
|
||||
"github.com/microsoft/typescript-go/internal/checker"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/module"
|
||||
"github.com/microsoft/typescript-go/internal/packagejson"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/symlinks"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
|
||||
"github.com/microsoft/typescript-go/internal/tsoptions"
|
||||
"github.com/microsoft/typescript-go/internal/tspath"
|
||||
)
|
||||
|
||||
type fakeProgram struct {
|
||||
singleThreaded bool
|
||||
compilerOptions *core.CompilerOptions
|
||||
files []*ast.SourceFile
|
||||
getEmitModuleFormatOfFile func(sourceFile ast.HasFileName) core.ModuleKind
|
||||
getImpliedNodeFormatForEmit func(sourceFile ast.HasFileName) core.ModuleKind
|
||||
getResolvedModule func(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule
|
||||
getSourceFile func(FileName string) *ast.SourceFile
|
||||
getSourceFileForResolvedModule func(FileName string) *ast.SourceFile
|
||||
}
|
||||
|
||||
// GetRedirectForResolution implements checker.Program.
|
||||
func (p *fakeProgram) GetRedirectForResolution(file ast.HasFileName) *tsoptions.ParsedCommandLine {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// SourceFileMayBeEmitted implements checker.Program.
|
||||
func (p *fakeProgram) SourceFileMayBeEmitted(sourceFile *ast.SourceFile, forceDtsEmit bool) bool {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// GetEmitSyntaxForUsageLocation implements checker.Program.
|
||||
func (p *fakeProgram) GetEmitSyntaxForUsageLocation(sourceFile ast.HasFileName, usageLocation *ast.StringLiteralLike) core.ResolutionMode {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
// CommonSourceDirectory implements checker.Program.
|
||||
func (p *fakeProgram) CommonSourceDirectory() string {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetResolvedModuleFromModuleSpecifier(file ast.HasFileName, moduleSpecifier *ast.StringLiteralLike) *module.ResolvedModule {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *fakeProgram) FileExists(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetCurrentDirectory() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetGlobalTypingsCacheLocation() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetNearestAncestorDirectoryWithPackageJson(dirname string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetSymlinkCache() *symlinks.KnownSymlinks {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) ResolveModuleName(moduleName string, containingFile string, resolutionMode core.ResolutionMode) *module.ResolvedModule {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetPackageJsonInfo(pkgJsonPath string) *packagejson.InfoCacheEntry {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetRedirectTargets(path tspath.Path) []string {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetSourceOfProjectReferenceIfOutputIncluded(file ast.HasFileName) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetProjectReferenceFromSource(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) IsSourceFromProjectReference(path tspath.Path) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetPackagesMap() map[string]bool {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetProjectReferenceFromOutputDts(path tspath.Path) *tsoptions.SourceOutputAndProjectReference {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) UseCaseSensitiveFileNames() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (p *fakeProgram) Options() *core.CompilerOptions {
|
||||
return p.compilerOptions
|
||||
}
|
||||
|
||||
func (p *fakeProgram) SourceFiles() []*ast.SourceFile {
|
||||
return p.files
|
||||
}
|
||||
|
||||
func (p *fakeProgram) BindSourceFiles() {
|
||||
wg := core.NewWorkGroup(p.singleThreaded)
|
||||
for _, file := range p.files {
|
||||
if !file.IsBound() {
|
||||
wg.Queue(func() {
|
||||
binder.BindSourceFile(file)
|
||||
})
|
||||
}
|
||||
}
|
||||
wg.RunAndWait()
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetEmitModuleFormatOfFile(sourceFile ast.HasFileName) core.ModuleKind {
|
||||
return p.getEmitModuleFormatOfFile(sourceFile)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetImpliedNodeFormatForEmit(sourceFile ast.HasFileName) core.ModuleKind {
|
||||
return p.getImpliedNodeFormatForEmit(sourceFile)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetDefaultResolutionModeForFile(sourceFile ast.HasFileName) core.ResolutionMode {
|
||||
return p.getEmitModuleFormatOfFile(sourceFile)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetModeForUsageLocation(sourceFile ast.HasFileName, location *ast.Node) core.ResolutionMode {
|
||||
return p.getEmitModuleFormatOfFile(sourceFile)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetResolvedModule(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule {
|
||||
return p.getResolvedModule(currentSourceFile, moduleReference, mode)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetSourceFile(FileName string) *ast.SourceFile {
|
||||
return p.getSourceFile(FileName)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetSourceFileForResolvedModule(FileName string) *ast.SourceFile {
|
||||
return p.getSourceFileForResolvedModule(FileName)
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetSourceFileMetaData(path tspath.Path) ast.SourceFileMetaData {
|
||||
return ast.SourceFileMetaData{}
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetImportHelpersImportSpecifier(path tspath.Path) *ast.Node {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetJSXRuntimeImportSpecifier(path tspath.Path) (moduleReference string, specifier *ast.Node) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (p *fakeProgram) GetResolvedModules() map[tspath.Path]module.ModeAwareCache[*module.ResolvedModule] {
|
||||
panic("unimplemented")
|
||||
}
|
||||
|
||||
func (p *fakeProgram) IsSourceFileDefaultLibrary(path tspath.Path) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func TestImportElision(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := []struct {
|
||||
title string
|
||||
input string
|
||||
output string
|
||||
other string
|
||||
jsx bool
|
||||
}{
|
||||
{title: "ImportEquals#1", input: "import x = require(\"other\"); x;", output: "import x = require(\"other\");\nx;"},
|
||||
{title: "ImportEquals#2", input: "import x = require(\"other\");", output: ""},
|
||||
{title: "ImportDeclaration#1", input: `import "m";`, output: `import "m";`},
|
||||
{title: "ImportDeclaration#2", input: "import * as x from \"other\"; x;", output: "import * as x from \"other\";\nx;"},
|
||||
{title: "ImportDeclaration#3", input: "import x from \"other\"; x;", output: "import x from \"other\";\nx;"},
|
||||
{title: "ImportDeclaration#4", input: "import { x } from \"other\"; x;", output: "import { x } from \"other\";\nx;"},
|
||||
{title: "ImportDeclaration#5", input: "import * as x from \"other\";", output: ""},
|
||||
{title: "ImportDeclaration#6", input: "import x from \"other\";", output: ""},
|
||||
{title: "ImportDeclaration#7", input: "import { x } from \"other\";", output: ""},
|
||||
{title: "ExportDeclaration#1", input: "export * from \"other\";", other: "export let x;", output: "export * from \"other\";"},
|
||||
{title: "ExportDeclaration#2", input: "export * as x from \"other\";", other: "export let x;", output: "export * as x from \"other\";"},
|
||||
{title: "ExportDeclaration#3", input: "export * from \"other\";", other: "export let x;", output: "export * from \"other\";"},
|
||||
{title: "ExportDeclaration#4", input: "export * as x from \"other\";", other: "export let x;", output: "export * as x from \"other\";"},
|
||||
{title: "ExportDeclaration#5", input: "export { x } from \"other\";", other: "export let x;", output: "export { x } from \"other\";"},
|
||||
{title: "ExportDeclaration#6", input: "export { x } from \"other\";", other: "export type x = any;", output: ""},
|
||||
{title: "ExportDeclaration#7", input: "export { x }; let x;", output: "export { x };\nlet x;"},
|
||||
{title: "ExportDeclaration#8", input: "export { x }; type x = any;", output: ""},
|
||||
{title: "ExportDeclaration#9", input: "import { x } from \"other\"; export { x };", other: "export type x = any;", output: ""},
|
||||
{title: "ExportAssignment#1", input: "let x; export default x;", output: "let x;\nexport default x;"},
|
||||
{title: "ExportAssignment#2", input: "type x = any; export default x;", output: ""},
|
||||
}
|
||||
|
||||
for _, rec := range data {
|
||||
t.Run(rec.title, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
file := parsetestutil.ParseTypeScript(rec.input, rec.jsx)
|
||||
parsetestutil.CheckDiagnostics(t, file)
|
||||
files := []*ast.SourceFile{file}
|
||||
|
||||
var other *ast.SourceFile
|
||||
if len(rec.other) > 0 {
|
||||
other = parsetestutil.ParseTypeScript(rec.other, rec.jsx)
|
||||
parsetestutil.CheckDiagnostics(t, other)
|
||||
files = append(files, other)
|
||||
}
|
||||
|
||||
compilerOptions := &core.CompilerOptions{}
|
||||
|
||||
c, _ := checker.NewChecker(&fakeProgram{
|
||||
singleThreaded: true,
|
||||
compilerOptions: compilerOptions,
|
||||
files: files,
|
||||
getEmitModuleFormatOfFile: func(sourceFile ast.HasFileName) core.ModuleKind {
|
||||
return core.ModuleKindESNext
|
||||
},
|
||||
getImpliedNodeFormatForEmit: func(sourceFile ast.HasFileName) core.ModuleKind {
|
||||
return core.ModuleKindESNext
|
||||
},
|
||||
getSourceFile: func(fileName string) *ast.SourceFile {
|
||||
if fileName == "other.ts" {
|
||||
return other
|
||||
}
|
||||
return nil
|
||||
},
|
||||
getSourceFileForResolvedModule: func(fileName string) *ast.SourceFile {
|
||||
if fileName == "other.ts" {
|
||||
return other
|
||||
}
|
||||
return nil
|
||||
},
|
||||
getResolvedModule: func(currentSourceFile ast.HasFileName, moduleReference string, mode core.ResolutionMode) *module.ResolvedModule {
|
||||
if currentSourceFile == file && moduleReference == "other" {
|
||||
return &module.ResolvedModule{
|
||||
ResolvedFileName: "other.ts",
|
||||
Extension: tspath.ExtensionTs,
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}, nil)
|
||||
|
||||
emitResolver := c.GetEmitResolver()
|
||||
|
||||
opts := &transformers.TransformOptions{CompilerOptions: compilerOptions, Context: printer.NewEmitContext(), EmitResolver: emitResolver, Resolver: emitResolver}
|
||||
file = tstransforms.NewTypeEraserTransformer(opts).TransformSourceFile(file)
|
||||
file = tstransforms.NewImportElisionTransformer(opts).TransformSourceFile(file)
|
||||
emittestutil.CheckEmit(t, nil, file, rec.output)
|
||||
})
|
||||
}
|
||||
}
|
||||
1038
tools/tsgo/internal/transformers/tstransforms/legacydecorators.go
Normal file
1038
tools/tsgo/internal/transformers/tstransforms/legacydecorators.go
Normal file
File diff suppressed because it is too large
Load Diff
390
tools/tsgo/internal/transformers/tstransforms/metadata.go
Normal file
390
tools/tsgo/internal/transformers/tstransforms/metadata.go
Normal file
@@ -0,0 +1,390 @@
|
||||
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"
|
||||
)
|
||||
|
||||
const USE_NEW_TYPE_METADATA_FORMAT = false
|
||||
|
||||
type MetadataTransformer struct {
|
||||
transformers.Transformer
|
||||
legacyDecorators bool
|
||||
resolver printer.EmitResolver
|
||||
|
||||
serializer *metadataSerializer
|
||||
languageVersion core.ScriptTarget
|
||||
strictNullChecks bool
|
||||
parent *ast.Node
|
||||
currentLexicalScope *ast.Node
|
||||
}
|
||||
|
||||
func NewMetadataTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
|
||||
tx := &MetadataTransformer{
|
||||
legacyDecorators: opt.CompilerOptions.ExperimentalDecorators.IsTrue(),
|
||||
resolver: opt.EmitResolver,
|
||||
languageVersion: opt.CompilerOptions.GetEmitScriptTarget(),
|
||||
strictNullChecks: opt.CompilerOptions.GetStrictOptionValue(opt.CompilerOptions.StrictNullChecks),
|
||||
}
|
||||
return tx.NewTransformer(tx.visit, opt.Context)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visit(node *ast.Node) *ast.Node {
|
||||
if (node.SubtreeFacts() & ast.SubtreeContainsDecorators) == 0 {
|
||||
return node
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindClassDeclaration:
|
||||
return tx.visitClassDeclaration(node.AsClassDeclaration())
|
||||
case ast.KindClassExpression:
|
||||
return tx.visitClassExpression(node.AsClassExpression())
|
||||
case ast.KindPropertyDeclaration:
|
||||
return tx.visitPropertyDeclaration(node.AsPropertyDeclaration())
|
||||
case ast.KindMethodDeclaration:
|
||||
return tx.visitMethodDeclaration(node.AsMethodDeclaration())
|
||||
case ast.KindSetAccessor:
|
||||
return tx.visitSetAccessor(node.AsSetAccessorDeclaration())
|
||||
case ast.KindGetAccessor:
|
||||
return tx.visitGetAccessor(node.AsGetAccessorDeclaration())
|
||||
case ast.KindSourceFile:
|
||||
tx.parent = nil
|
||||
defer tx.setParent(nil)
|
||||
tx.currentLexicalScope = node
|
||||
defer tx.setCurrentLexicalScope(nil)
|
||||
tx.serializer = newMetadataSerializer(tx.resolver, tx.Factory(), tx.EmitContext(), tx.languageVersion, tx.strictNullChecks)
|
||||
updated := tx.Visitor().VisitEachChild(node)
|
||||
tx.EmitContext().AddEmitHelper(updated, tx.EmitContext().ReadEmitHelpers()...)
|
||||
return updated
|
||||
case ast.KindModuleBlock, ast.KindBlock, ast.KindCaseBlock:
|
||||
oldScope := tx.currentLexicalScope
|
||||
tx.currentLexicalScope = node
|
||||
defer tx.setCurrentLexicalScope(oldScope)
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
default:
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) setParent(node *ast.Node) {
|
||||
tx.parent = node
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) setCurrentLexicalScope(node *ast.Node) {
|
||||
tx.currentLexicalScope = node
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitClassExpression(node *ast.ClassExpression) *ast.Node {
|
||||
oldParent := tx.parent
|
||||
tx.parent = node.AsNode()
|
||||
defer tx.setParent(oldParent)
|
||||
|
||||
if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode())
|
||||
return tx.Factory().UpdateClassExpression(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNodes(node.TypeParameters),
|
||||
tx.Visitor().VisitNodes(node.HeritageClauses),
|
||||
tx.Visitor().VisitNodes(node.Members),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitClassDeclaration(node *ast.ClassDeclaration) *ast.Node {
|
||||
oldParent := tx.parent
|
||||
tx.parent = node.AsNode()
|
||||
defer tx.setParent(oldParent)
|
||||
|
||||
if !ast.ClassOrConstructorParameterIsDecorated(tx.legacyDecorators, node.AsNode()) {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
modifiers := tx.injectClassTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode())
|
||||
return tx.Factory().UpdateClassDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNodes(node.TypeParameters),
|
||||
tx.Visitor().VisitNodes(node.HeritageClauses),
|
||||
tx.Visitor().VisitNodes(node.Members),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitPropertyDeclaration(node *ast.PropertyDeclaration) *ast.Node {
|
||||
if !ast.HasDecorators(node.AsNode()) {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
|
||||
return tx.Factory().UpdatePropertyDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNode(node.PostfixToken),
|
||||
tx.Visitor().VisitNode(node.Type),
|
||||
tx.Visitor().VisitNode(node.Initializer),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitMethodDeclaration(node *ast.MethodDeclaration) *ast.Node {
|
||||
if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
|
||||
return tx.Factory().UpdateMethodDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.AsteriskToken),
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNode(node.PostfixToken),
|
||||
tx.Visitor().VisitNodes(node.TypeParameters),
|
||||
tx.Visitor().VisitNodes(node.Parameters),
|
||||
tx.Visitor().VisitNode(node.Type),
|
||||
tx.Visitor().VisitNode(node.FullSignature),
|
||||
tx.Visitor().VisitNode(node.Body),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitSetAccessor(node *ast.SetAccessorDeclaration) *ast.Node {
|
||||
if !ast.HasDecorators(node.AsNode()) && len(getDecoratorsOfParameters(node.AsNode())) == 0 {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
|
||||
return tx.Factory().UpdateSetAccessorDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNodes(node.TypeParameters),
|
||||
tx.Visitor().VisitNodes(node.Parameters),
|
||||
tx.Visitor().VisitNode(node.Type),
|
||||
tx.Visitor().VisitNode(node.FullSignature),
|
||||
tx.Visitor().VisitNode(node.Body),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) visitGetAccessor(node *ast.GetAccessorDeclaration) *ast.Node {
|
||||
if !ast.HasDecorators(node.AsNode()) {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
modifiers := tx.injectClassElementTypeMetadata(tx.Visitor().VisitModifiers(node.Modifiers()), node.AsNode(), tx.parent)
|
||||
return tx.Factory().UpdateGetAccessorDeclaration(
|
||||
node,
|
||||
modifiers,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
tx.Visitor().VisitNodes(node.TypeParameters),
|
||||
tx.Visitor().VisitNodes(node.Parameters),
|
||||
tx.Visitor().VisitNode(node.Type),
|
||||
tx.Visitor().VisitNode(node.FullSignature),
|
||||
tx.Visitor().VisitNode(node.Body),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) injectClassTypeMetadata(list *ast.ModifierList, node *ast.Node) *ast.ModifierList {
|
||||
metadata := tx.getTypeMetadata(node, node)
|
||||
if len(metadata) > 0 {
|
||||
var originalNodes []*ast.Node
|
||||
if list != nil {
|
||||
originalNodes = list.Nodes
|
||||
}
|
||||
if len(originalNodes) == 0 {
|
||||
res := tx.Factory().NewModifierList(metadata)
|
||||
if list != nil {
|
||||
res.Loc = list.Loc
|
||||
}
|
||||
return res
|
||||
}
|
||||
var modifiersArray []*ast.Node
|
||||
if ast.IsModifier(originalNodes[0]) && (originalNodes[0].Kind == ast.KindDefaultKeyword || originalNodes[0].Kind == ast.KindExportKeyword) {
|
||||
modifiersArray = append(modifiersArray, originalNodes[0])
|
||||
if len(originalNodes) > 1 && (originalNodes[1].Kind == ast.KindDefaultKeyword || originalNodes[1].Kind == ast.KindExportKeyword) {
|
||||
modifiersArray = append(modifiersArray, originalNodes[1])
|
||||
}
|
||||
}
|
||||
restStart := len(modifiersArray)
|
||||
decos := core.Filter(originalNodes, ast.IsDecorator)
|
||||
modifiersArray = append(modifiersArray, decos...)
|
||||
modifiersArray = append(modifiersArray, metadata...)
|
||||
otherModifiers := core.Filter(originalNodes[restStart:], ast.IsModifier)
|
||||
modifiersArray = append(modifiersArray, otherModifiers...)
|
||||
res := tx.Factory().NewModifierList(modifiersArray)
|
||||
res.Loc = list.Loc
|
||||
return res
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) injectClassElementTypeMetadata(list *ast.ModifierList, node *ast.Node, container *ast.Node) *ast.ModifierList {
|
||||
if !ast.IsClassLike(container) {
|
||||
return list
|
||||
}
|
||||
if !ast.ClassElementOrClassElementParameterIsDecorated(tx.legacyDecorators, node, container) {
|
||||
return list
|
||||
}
|
||||
metadata := tx.getTypeMetadata(node, container)
|
||||
if len(metadata) > 0 {
|
||||
var originalNodes []*ast.Node
|
||||
if list != nil {
|
||||
originalNodes = list.Nodes
|
||||
}
|
||||
if len(originalNodes) == 0 {
|
||||
res := tx.Factory().NewModifierList(metadata)
|
||||
if list != nil {
|
||||
res.Loc = list.Loc
|
||||
}
|
||||
return res
|
||||
}
|
||||
var modifiersArray []*ast.Node
|
||||
decos := core.Filter(originalNodes, ast.IsDecorator)
|
||||
modifiersArray = append(modifiersArray, decos...)
|
||||
modifiersArray = append(modifiersArray, metadata...)
|
||||
modifiers := core.Filter(originalNodes, ast.IsModifier)
|
||||
modifiersArray = append(modifiersArray, modifiers...)
|
||||
res := tx.Factory().NewModifierList(modifiersArray)
|
||||
res.Loc = list.Loc
|
||||
return res
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets optional type metadata for a declaration.
|
||||
*
|
||||
* @param node The declaration node.
|
||||
*/
|
||||
func (tx *MetadataTransformer) getTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
|
||||
// Decorator metadata is not yet supported for ES decorators.
|
||||
if !tx.legacyDecorators {
|
||||
return nil
|
||||
}
|
||||
if USE_NEW_TYPE_METADATA_FORMAT {
|
||||
return tx.getNewTypeMetadata(node, container)
|
||||
}
|
||||
return tx.getOldTypeMetadata(node, container)
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) getOldTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
|
||||
var decorators []*ast.Node
|
||||
if tx.shouldAddTypeMetadata(node) {
|
||||
typeMetadata := tx.Factory().NewMetadataHelper("design:type", tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container))
|
||||
decorators = append(decorators, tx.Factory().NewDecorator(typeMetadata))
|
||||
}
|
||||
if tx.shouldAddParamTypesMetadata(node) {
|
||||
paramTypesMetadata := tx.Factory().NewMetadataHelper("design:paramtypes", tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container))
|
||||
decorators = append(decorators, tx.Factory().NewDecorator(paramTypesMetadata))
|
||||
}
|
||||
if tx.shouldAddReturnTypeMetadata(node) {
|
||||
returnTypeMetadata := tx.Factory().NewMetadataHelper("design:returntype", tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node))
|
||||
decorators = append(decorators, tx.Factory().NewDecorator(returnTypeMetadata))
|
||||
}
|
||||
return decorators
|
||||
}
|
||||
|
||||
func (tx *MetadataTransformer) getNewTypeMetadata(node *ast.Node, container *ast.Node) []*ast.Node {
|
||||
var properties []*ast.Node
|
||||
if tx.shouldAddTypeMetadata(node) {
|
||||
properties = append(properties, tx.Factory().NewPropertyAssignment(
|
||||
nil,
|
||||
tx.Factory().NewIdentifier("type"),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewArrowFunction(
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewNodeList([]*ast.Node{}),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
|
||||
tx.serializer.SerializeTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container),
|
||||
),
|
||||
))
|
||||
}
|
||||
if tx.shouldAddParamTypesMetadata(node) {
|
||||
properties = append(properties, tx.Factory().NewPropertyAssignment(
|
||||
nil,
|
||||
tx.Factory().NewIdentifier("paramTypes"),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewArrowFunction(
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewNodeList([]*ast.Node{}),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
|
||||
tx.serializer.SerializeParameterTypesOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node, container),
|
||||
),
|
||||
))
|
||||
}
|
||||
if tx.shouldAddReturnTypeMetadata(node) {
|
||||
properties = append(properties, tx.Factory().NewPropertyAssignment(
|
||||
nil,
|
||||
tx.Factory().NewIdentifier("returnType"),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewArrowFunction(
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewNodeList([]*ast.Node{}),
|
||||
nil,
|
||||
nil,
|
||||
tx.Factory().NewToken(ast.KindEqualsGreaterThanToken),
|
||||
tx.serializer.SerializeReturnTypeOfNode(metadataSerializerContext{currentLexicalScope: tx.currentLexicalScope, currentNameScope: container}, node),
|
||||
),
|
||||
))
|
||||
}
|
||||
if len(properties) > 0 {
|
||||
typeInfoMetadata := tx.Factory().NewMetadataHelper("design:typeinfo", tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList(properties), true))
|
||||
return []*ast.Node{tx.Factory().NewDecorator(typeInfoMetadata)}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit the "design:type" metadata based on the node's kind.
|
||||
* The caller should have already tested whether the node has decorators and whether the
|
||||
* emitDecoratorMetadata compiler option is set.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
func (tx *MetadataTransformer) shouldAddTypeMetadata(node *ast.Node) bool {
|
||||
switch node.Kind {
|
||||
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyDeclaration:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit the "design:returntype" metadata based on the node's kind.
|
||||
* The caller should have already tested whether the node has decorators and whether the
|
||||
* emitDecoratorMetadata compiler option is set.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
func (tx *MetadataTransformer) shouldAddReturnTypeMetadata(node *ast.Node) bool {
|
||||
return node.Kind == ast.KindMethodDeclaration
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether to emit the "design:paramtypes" metadata based on the node's kind.
|
||||
* The caller should have already tested whether the node has decorators and whether the
|
||||
* emitDecoratorMetadata compiler option is set.
|
||||
*
|
||||
* @param node The node to test.
|
||||
*/
|
||||
func (tx *MetadataTransformer) shouldAddParamTypesMetadata(node *ast.Node) bool {
|
||||
switch node.Kind {
|
||||
case ast.KindClassDeclaration, ast.KindClassExpression:
|
||||
return ast.GetFirstConstructorWithBody(node) != nil
|
||||
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
995
tools/tsgo/internal/transformers/tstransforms/runtimesyntax.go
Normal file
995
tools/tsgo/internal/transformers/tstransforms/runtimesyntax.go
Normal file
@@ -0,0 +1,995 @@
|
||||
package tstransforms
|
||||
|
||||
// !!! SourceMaps and Comments need to be validated
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/binder"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/jsnum"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
)
|
||||
|
||||
// Transforms TypeScript-specific runtime syntax into JavaScript-compatible syntax.
|
||||
type RuntimeSyntaxTransformer struct {
|
||||
transformers.Transformer
|
||||
compilerOptions *core.CompilerOptions
|
||||
parentNode *ast.Node
|
||||
currentNode *ast.Node
|
||||
currentSourceFile *ast.Node
|
||||
currentScope *ast.Node // SourceFile | Block | ModuleBlock | CaseBlock
|
||||
currentScopeFirstDeclarationsOfName map[string]*ast.Node
|
||||
currentEnum *ast.EnumDeclarationNode
|
||||
currentNamespace *ast.ModuleDeclarationNode
|
||||
resolver binder.ReferenceResolver
|
||||
emitResolver printer.EmitResolver
|
||||
}
|
||||
|
||||
func NewRuntimeSyntaxTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
|
||||
compilerOptions := opt.CompilerOptions
|
||||
emitContext := opt.Context
|
||||
tx := &RuntimeSyntaxTransformer{compilerOptions: compilerOptions, resolver: opt.Resolver, emitResolver: opt.EmitResolver}
|
||||
return tx.NewTransformer(tx.visit, emitContext)
|
||||
}
|
||||
|
||||
// Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.
|
||||
func (tx *RuntimeSyntaxTransformer) pushNode(node *ast.Node) (grandparentNode *ast.Node) {
|
||||
grandparentNode = tx.parentNode
|
||||
tx.parentNode = tx.currentNode
|
||||
tx.currentNode = node
|
||||
return grandparentNode
|
||||
}
|
||||
|
||||
// Pops the last child node off the ancestor tracking stack, restoring the grandparent node.
|
||||
func (tx *RuntimeSyntaxTransformer) popNode(grandparentNode *ast.Node) {
|
||||
tx.currentNode = tx.parentNode
|
||||
tx.parentNode = grandparentNode
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) pushScope(node *ast.Node) (savedCurrentScope *ast.Node, savedCurrentScopeFirstDeclarationsOfName map[string]*ast.Node) {
|
||||
savedCurrentScope = tx.currentScope
|
||||
savedCurrentScopeFirstDeclarationsOfName = tx.currentScopeFirstDeclarationsOfName
|
||||
switch node.Kind {
|
||||
case ast.KindSourceFile:
|
||||
tx.currentScope = node
|
||||
tx.currentSourceFile = node
|
||||
tx.currentScopeFirstDeclarationsOfName = nil
|
||||
case ast.KindCaseBlock, ast.KindModuleBlock, ast.KindBlock:
|
||||
tx.currentScope = node
|
||||
tx.currentScopeFirstDeclarationsOfName = nil
|
||||
case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindVariableStatement:
|
||||
tx.recordDeclarationInScope(node)
|
||||
}
|
||||
return savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) popScope(savedCurrentScope *ast.Node, savedCurrentScopeFirstDeclarationsOfName map[string]*ast.Node) {
|
||||
if tx.currentScope != savedCurrentScope {
|
||||
// only reset the first declaration for a name if we are exiting the scope in which it was declared
|
||||
tx.currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName
|
||||
}
|
||||
|
||||
tx.currentScope = savedCurrentScope
|
||||
}
|
||||
|
||||
// Visits each node in the AST
|
||||
func (tx *RuntimeSyntaxTransformer) visit(node *ast.Node) *ast.Node {
|
||||
grandparentNode := tx.pushNode(node)
|
||||
defer tx.popNode(grandparentNode)
|
||||
|
||||
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(node)
|
||||
defer tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
|
||||
|
||||
if node.SubtreeFacts()&ast.SubtreeContainsTypeScript == 0 && (tx.currentNamespace == nil && tx.currentEnum == nil || node.SubtreeFacts()&ast.SubtreeContainsIdentifier == 0) {
|
||||
return node
|
||||
}
|
||||
|
||||
switch node.Kind {
|
||||
// TypeScript parameter property modifiers are elided
|
||||
case ast.KindPublicKeyword,
|
||||
ast.KindPrivateKeyword,
|
||||
ast.KindProtectedKeyword,
|
||||
ast.KindReadonlyKeyword,
|
||||
ast.KindOverrideKeyword:
|
||||
node = nil
|
||||
case ast.KindEnumDeclaration:
|
||||
node = tx.visitEnumDeclaration(node.AsEnumDeclaration())
|
||||
case ast.KindModuleDeclaration:
|
||||
node = tx.visitModuleDeclaration(node.AsModuleDeclaration())
|
||||
case ast.KindClassDeclaration:
|
||||
node = tx.visitClassDeclaration(node.AsClassDeclaration())
|
||||
case ast.KindClassExpression:
|
||||
node = tx.visitClassExpression(node.AsClassExpression())
|
||||
case ast.KindConstructor:
|
||||
node = tx.visitConstructorDeclaration(node.AsConstructorDeclaration())
|
||||
case ast.KindFunctionDeclaration:
|
||||
node = tx.visitFunctionDeclaration(node.AsFunctionDeclaration())
|
||||
case ast.KindVariableStatement:
|
||||
node = tx.visitVariableStatement(node.AsVariableStatement())
|
||||
case ast.KindExportDeclaration, ast.KindImportDeclaration, ast.KindImportClause:
|
||||
if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind != ast.KindBlock {
|
||||
// do not emit ES6 imports and exports since they are illegal inside a namespace
|
||||
node = nil
|
||||
} else {
|
||||
node = tx.Visitor().VisitEachChild(node)
|
||||
}
|
||||
case ast.KindImportEqualsDeclaration:
|
||||
if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind != ast.KindBlock && node.AsImportEqualsDeclaration().ModuleReference.Kind == ast.KindExternalModuleReference {
|
||||
// do not emit ES6 imports and exports since they are illegal inside a namespace
|
||||
node = nil
|
||||
} else if tx.currentNamespace != nil && tx.currentScope != nil && tx.currentScope.Kind == ast.KindBlock && node.AsImportEqualsDeclaration().ModuleReference.Kind != ast.KindExternalModuleReference {
|
||||
// inside a block within a namespace, elide internal import aliases
|
||||
node = nil
|
||||
} else {
|
||||
node = tx.visitImportEqualsDeclaration(node.AsImportEqualsDeclaration())
|
||||
}
|
||||
case ast.KindIdentifier:
|
||||
node = tx.visitIdentifier(node)
|
||||
case ast.KindShorthandPropertyAssignment:
|
||||
node = tx.visitShorthandPropertyAssignment(node.AsShorthandPropertyAssignment())
|
||||
default:
|
||||
node = tx.Visitor().VisitEachChild(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
// Records that a declaration was emitted in the current scope, if it was the first declaration for the provided symbol.
|
||||
func (tx *RuntimeSyntaxTransformer) recordDeclarationInScope(node *ast.Node) {
|
||||
switch node.Kind {
|
||||
case ast.KindVariableStatement:
|
||||
tx.recordDeclarationInScope(node.AsVariableStatement().DeclarationList)
|
||||
return
|
||||
case ast.KindVariableDeclarationList:
|
||||
for _, decl := range node.AsVariableDeclarationList().Declarations.Nodes {
|
||||
tx.recordDeclarationInScope(decl)
|
||||
}
|
||||
return
|
||||
case ast.KindArrayBindingPattern, ast.KindObjectBindingPattern:
|
||||
for _, element := range node.Elements() {
|
||||
tx.recordDeclarationInScope(element)
|
||||
}
|
||||
return
|
||||
}
|
||||
name := node.Name()
|
||||
if name != nil {
|
||||
if ast.IsIdentifier(name) {
|
||||
if tx.currentScopeFirstDeclarationsOfName == nil {
|
||||
tx.currentScopeFirstDeclarationsOfName = make(map[string]*ast.Node)
|
||||
}
|
||||
text := name.Text()
|
||||
if _, found := tx.currentScopeFirstDeclarationsOfName[text]; !found {
|
||||
tx.currentScopeFirstDeclarationsOfName[text] = node
|
||||
}
|
||||
} else if ast.IsBindingPattern(name) {
|
||||
tx.recordDeclarationInScope(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determines whether a declaration is the first declaration with the same name emitted in the current scope.
|
||||
func (tx *RuntimeSyntaxTransformer) isFirstDeclarationInScope(node *ast.Node) bool {
|
||||
name := node.Name()
|
||||
if name != nil && ast.IsIdentifier(name) {
|
||||
text := name.Text()
|
||||
if firstDeclaration, found := tx.currentScopeFirstDeclarationsOfName[text]; found {
|
||||
return firstDeclaration == node
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) isExportOfNamespace(node *ast.Node) bool {
|
||||
return tx.currentNamespace != nil && (tx.currentScope == nil || tx.currentScope.Kind != ast.KindBlock) && node.ModifierFlags()&ast.ModifierFlagsExport != 0
|
||||
}
|
||||
|
||||
// Gets an expression that represents a property name, such as `"foo"` for the identifier `foo`.
|
||||
func (tx *RuntimeSyntaxTransformer) getExpressionForPropertyName(member *ast.EnumMember) *ast.Expression {
|
||||
name := member.Name()
|
||||
switch name.Kind {
|
||||
case ast.KindPrivateIdentifier:
|
||||
return tx.Factory().NewIdentifier("")
|
||||
case ast.KindComputedPropertyName:
|
||||
n := name.AsComputedPropertyName()
|
||||
// enums don't support computed properties so we always generate the 'expression' part of the name as-is.
|
||||
return tx.Visitor().VisitNode(n.Expression)
|
||||
case ast.KindIdentifier:
|
||||
return tx.Factory().NewStringLiteral(name.Text(), ast.TokenFlagsNone)
|
||||
case ast.KindStringLiteral: // !!! propagate token flags (will produce new diffs)
|
||||
return tx.Factory().NewStringLiteral(name.Text(), ast.TokenFlagsNone)
|
||||
case ast.KindNumericLiteral:
|
||||
return tx.Factory().NewNumericLiteral(name.Text(), ast.TokenFlagsNone)
|
||||
default:
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
// Gets an expression like `E["A"]` that references an enum member.
|
||||
func (tx *RuntimeSyntaxTransformer) getEnumQualifiedElement(enum *ast.EnumDeclaration, member *ast.EnumMember) *ast.Expression {
|
||||
prop := tx.getNamespaceQualifiedElement(tx.getNamespaceContainerName(enum.AsNode()), tx.getExpressionForPropertyName(member))
|
||||
tx.EmitContext().AddEmitFlags(prop, printer.EFNoComments|printer.EFNoNestedComments|printer.EFNoSourceMap|printer.EFNoNestedSourceMaps)
|
||||
return prop
|
||||
}
|
||||
|
||||
// Gets an expression used to refer to a namespace or enum from within the body of its declaration.
|
||||
func (tx *RuntimeSyntaxTransformer) getNamespaceContainerName(node *ast.Node) *ast.IdentifierNode {
|
||||
return tx.Factory().NewGeneratedNameForNode(node)
|
||||
}
|
||||
|
||||
// Gets an expression used to refer to an export of a namespace or a member of an enum by property name.
|
||||
func (tx *RuntimeSyntaxTransformer) getNamespaceQualifiedProperty(ns *ast.IdentifierNode, name *ast.IdentifierNode) *ast.Expression {
|
||||
return tx.Factory().GetNamespaceMemberName(ns, name, printer.NameOptions{AllowSourceMaps: true})
|
||||
}
|
||||
|
||||
// Gets an expression used to refer to an export of a namespace or a member of an enum by indexed access.
|
||||
func (tx *RuntimeSyntaxTransformer) getNamespaceQualifiedElement(ns *ast.IdentifierNode, expression *ast.Expression) *ast.Expression {
|
||||
qualifiedName := tx.EmitContext().Factory.NewElementAccessExpression(ns, nil /*questionDotToken*/, expression, ast.NodeFlagsNone)
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(qualifiedName, expression)
|
||||
return qualifiedName
|
||||
}
|
||||
|
||||
// Gets an expression used within the provided node's container for any exported references.
|
||||
func (tx *RuntimeSyntaxTransformer) getExportQualifiedReferenceToDeclaration(node *ast.Declaration) *ast.Expression {
|
||||
if tx.isExportOfNamespace(node.AsNode()) {
|
||||
return tx.Factory().GetExternalModuleOrNamespaceExportName(tx.getNamespaceContainerName(tx.currentNamespace), node, false /*allowComments*/, true /*allowSourceMaps*/)
|
||||
}
|
||||
return tx.Factory().GetDeclarationNameEx(node.AsNode(), printer.NameOptions{AllowSourceMaps: true})
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) addVarForDeclaration(statements []*ast.Statement, node *ast.Declaration) ([]*ast.Statement, bool) {
|
||||
tx.recordDeclarationInScope(node)
|
||||
if !tx.isFirstDeclarationInScope(node) {
|
||||
return statements, false
|
||||
}
|
||||
|
||||
// var name;
|
||||
name := tx.Factory().GetLocalNameEx(node, printer.AssignedNameOptions{AllowSourceMaps: true})
|
||||
varDecl := tx.Factory().NewVariableDeclaration(name, nil, nil, nil)
|
||||
varFlags := core.IfElse(tx.currentScope == tx.currentSourceFile, ast.NodeFlagsNone, ast.NodeFlagsLet)
|
||||
varDecls := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{varDecl}), varFlags)
|
||||
// Replicate modifierVisitor: strip decorators, TypeScript modifiers, and export when in namespace.
|
||||
modifierMask := ^(ast.ModifierFlagsTypeScriptModifier | ast.ModifierFlagsDecorator)
|
||||
if tx.currentNamespace != nil {
|
||||
modifierMask &^= ast.ModifierFlagsExport
|
||||
}
|
||||
modifiers := transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), modifierMask)
|
||||
varStatement := tx.Factory().NewVariableStatement(modifiers, varDecls)
|
||||
|
||||
tx.EmitContext().SetOriginal(varDecl, node)
|
||||
// !!! synthetic comments
|
||||
tx.EmitContext().SetOriginal(varStatement, node)
|
||||
|
||||
// Adjust the source map emit to match the old emitter.
|
||||
if ast.IsEnumDeclaration(node) {
|
||||
tx.EmitContext().SetSourceMapRange(varDecls, node.Loc)
|
||||
} else {
|
||||
tx.EmitContext().SetSourceMapRange(varStatement, node.Loc)
|
||||
}
|
||||
|
||||
// Trailing comments for enum declaration should be emitted after the function closure
|
||||
// instead of the variable statement:
|
||||
//
|
||||
// /** Leading comment*/
|
||||
// enum E {
|
||||
// A
|
||||
// } // trailing comment
|
||||
//
|
||||
// Should emit:
|
||||
//
|
||||
// /** Leading comment*/
|
||||
// var E;
|
||||
// (function (E) {
|
||||
// E[E["A"] = 0] = "A";
|
||||
// })(E || (E = {})); // trailing comment
|
||||
//
|
||||
tx.EmitContext().SetCommentRange(varStatement, node.Loc)
|
||||
tx.EmitContext().AddEmitFlags(varStatement, printer.EFNoTrailingComments)
|
||||
statements = append(statements, varStatement)
|
||||
|
||||
return statements, true
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitEnumDeclaration(node *ast.EnumDeclaration) *ast.Node {
|
||||
if !tx.shouldEmitEnumDeclaration(node) {
|
||||
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
|
||||
}
|
||||
|
||||
statements := []*ast.Statement{}
|
||||
|
||||
// If needed, we should emit a variable declaration for the enum:
|
||||
// var name;
|
||||
statements, varAdded := tx.addVarForDeclaration(statements, node.AsNode())
|
||||
|
||||
// If we emit a leading variable declaration, we should not emit leading comments for the enum body, but we should
|
||||
// still emit the comments if we are emitting to a System module.
|
||||
emitFlags := printer.EFNone
|
||||
if varAdded && (tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindSystem || tx.currentScope != tx.currentSourceFile) {
|
||||
emitFlags |= printer.EFNoLeadingComments
|
||||
}
|
||||
|
||||
// x || (x = {})
|
||||
// exports.x || (exports.x = {})
|
||||
enumArg := tx.Factory().NewLogicalORExpression(
|
||||
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
|
||||
tx.Factory().NewAssignmentExpression(
|
||||
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
|
||||
tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false),
|
||||
),
|
||||
)
|
||||
|
||||
if tx.isExportOfNamespace(node.AsNode()) {
|
||||
// `localName` is the expression used within this node's containing scope for any local references.
|
||||
localName := tx.Factory().GetLocalNameEx(node.AsNode(), printer.AssignedNameOptions{AllowSourceMaps: true})
|
||||
|
||||
// x = (exports.x || (exports.x = {}))
|
||||
enumArg = tx.Factory().NewAssignmentExpression(localName, enumArg)
|
||||
}
|
||||
|
||||
// (function (name) { ... })(name || (name = {}))
|
||||
enumParamName := tx.Factory().NewGeneratedNameForNode(node.AsNode())
|
||||
tx.EmitContext().SetSourceMapRange(enumParamName, node.Name().Loc)
|
||||
|
||||
enumParam := tx.Factory().NewParameterDeclaration(nil, nil, enumParamName, nil, nil, nil)
|
||||
enumBody := tx.transformEnumBody(node)
|
||||
enumFunc := tx.Factory().NewFunctionExpression(nil, nil, nil, nil, tx.Factory().NewNodeList([]*ast.Node{enumParam}), nil, nil, enumBody)
|
||||
enumCall := tx.Factory().NewCallExpression(tx.Factory().NewParenthesizedExpression(enumFunc), nil, nil, tx.Factory().NewNodeList([]*ast.Node{enumArg}), ast.NodeFlagsNone)
|
||||
enumStatement := tx.Factory().NewExpressionStatement(enumCall)
|
||||
tx.EmitContext().SetOriginal(enumStatement, node.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(enumStatement, node.AsNode())
|
||||
tx.EmitContext().AddEmitFlags(enumStatement, emitFlags)
|
||||
return tx.Factory().NewSyntaxList(append(statements, enumStatement))
|
||||
}
|
||||
|
||||
// Transforms the body of an enum declaration.
|
||||
func (tx *RuntimeSyntaxTransformer) transformEnumBody(node *ast.EnumDeclaration) *ast.BlockNode {
|
||||
savedCurrentEnum := tx.currentEnum
|
||||
tx.currentEnum = node.AsNode()
|
||||
|
||||
// visit the children of `node` in advance to capture any references to enum members
|
||||
node = tx.Visitor().VisitEachChild(node.AsNode()).AsEnumDeclaration()
|
||||
|
||||
statements := []*ast.Statement{}
|
||||
for i := range len(node.Members.Nodes) {
|
||||
// E[E["A"] = 0] = "A";
|
||||
statements = tx.transformEnumMember(
|
||||
statements,
|
||||
node,
|
||||
i,
|
||||
)
|
||||
}
|
||||
|
||||
statementList := tx.Factory().NewNodeList(statements)
|
||||
statementList.Loc = node.Members.Loc
|
||||
|
||||
tx.currentEnum = savedCurrentEnum
|
||||
return tx.Factory().NewBlock(statementList, true /*multiline*/)
|
||||
}
|
||||
|
||||
// Transforms an enum member into a statement. It is expected that `enum` has already been visited.
|
||||
func (tx *RuntimeSyntaxTransformer) transformEnumMember(
|
||||
statements []*ast.Statement,
|
||||
enum *ast.EnumDeclaration,
|
||||
index int,
|
||||
) []*ast.Statement {
|
||||
memberNode := enum.Members.Nodes[index]
|
||||
member := memberNode.AsEnumMember()
|
||||
|
||||
savedParent := tx.parentNode
|
||||
tx.parentNode = tx.currentNode
|
||||
tx.currentNode = memberNode
|
||||
|
||||
// E[E["A"] = x] = "A";
|
||||
// ^
|
||||
expression := member.Initializer // NOTE: already visited
|
||||
|
||||
var useExplicitReverseMapping bool
|
||||
|
||||
parseNode := tx.EmitContext().ParseNode(memberNode)
|
||||
result := tx.emitResolver.GetEnumMemberValue(parseNode)
|
||||
switch value := result.Value.(type) {
|
||||
case jsnum.Number:
|
||||
expression = core.Coalesce(constantExpression(value, tx.Factory()), expression)
|
||||
useExplicitReverseMapping = true
|
||||
case string:
|
||||
expression = core.Coalesce(constantExpression(value, tx.Factory()), expression)
|
||||
default:
|
||||
if expression == nil {
|
||||
expression = tx.Factory().NewVoidZeroExpression()
|
||||
}
|
||||
useExplicitReverseMapping = !result.IsSyntacticallyString
|
||||
}
|
||||
|
||||
// Define the enum member property:
|
||||
// E[E["A"] = 0] = "A";
|
||||
// ^^^^^^^^--_____
|
||||
expression = tx.Factory().NewAssignmentExpression(
|
||||
tx.getEnumQualifiedElement(enum, member),
|
||||
expression,
|
||||
)
|
||||
|
||||
if useExplicitReverseMapping {
|
||||
// E[E["A"] = 0] = "A";
|
||||
// ^^--------------^^^^^
|
||||
expression = tx.Factory().NewAssignmentExpression(
|
||||
tx.Factory().NewElementAccessExpression(
|
||||
tx.getNamespaceContainerName(enum.AsNode()),
|
||||
nil, /*questionDotToken*/
|
||||
expression,
|
||||
ast.NodeFlagsNone,
|
||||
),
|
||||
tx.getExpressionForPropertyName(member),
|
||||
)
|
||||
}
|
||||
|
||||
memberStatement := tx.Factory().NewExpressionStatement(expression)
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, member.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(memberStatement, member.AsNode())
|
||||
statements = append(statements, memberStatement)
|
||||
|
||||
tx.currentNode = tx.parentNode
|
||||
tx.parentNode = savedParent
|
||||
return statements
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitModuleDeclaration(node *ast.ModuleDeclaration) *ast.Node {
|
||||
if !tx.shouldEmitModuleDeclaration(node) {
|
||||
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
|
||||
}
|
||||
|
||||
statements := []*ast.Statement{}
|
||||
|
||||
// If needed, we should emit a variable declaration for the module:
|
||||
// var name;
|
||||
statements, varAdded := tx.addVarForDeclaration(statements, node.AsNode())
|
||||
|
||||
// If we emit a leading variable declaration, we should not emit leading comments for the module body, but we should
|
||||
// still emit the comments if we are emitting to a System module.
|
||||
emitFlags := printer.EFNone
|
||||
if varAdded && (tx.compilerOptions.GetEmitModuleKind() != core.ModuleKindSystem || tx.currentScope != tx.currentSourceFile) {
|
||||
emitFlags |= printer.EFNoLeadingComments
|
||||
}
|
||||
|
||||
// x || (x = {})
|
||||
// exports.x || (exports.x = {})
|
||||
moduleArg := tx.Factory().NewLogicalORExpression(
|
||||
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
|
||||
tx.Factory().NewAssignmentExpression(
|
||||
tx.getExportQualifiedReferenceToDeclaration(node.AsNode()),
|
||||
tx.Factory().NewObjectLiteralExpression(tx.Factory().NewNodeList([]*ast.Node{}), false),
|
||||
),
|
||||
)
|
||||
|
||||
if tx.isExportOfNamespace(node.AsNode()) {
|
||||
// `localName` is the expression used within this node's containing scope for any local references.
|
||||
localName := tx.Factory().GetLocalNameEx(node.AsNode(), printer.AssignedNameOptions{AllowSourceMaps: true})
|
||||
|
||||
// x = (exports.x || (exports.x = {}))
|
||||
moduleArg = tx.Factory().NewAssignmentExpression(localName, moduleArg)
|
||||
}
|
||||
|
||||
// (function (name) { ... })(name || (name = {}))
|
||||
moduleParamName := tx.Factory().NewGeneratedNameForNode(node.AsNode())
|
||||
tx.EmitContext().SetSourceMapRange(moduleParamName, node.Name().Loc)
|
||||
|
||||
moduleParam := tx.Factory().NewParameterDeclaration(nil, nil, moduleParamName, nil, nil, nil)
|
||||
moduleBody := tx.transformModuleBody(node, tx.getNamespaceContainerName(node.AsNode()))
|
||||
moduleFunc := tx.Factory().NewFunctionExpression(nil, nil, nil, nil, tx.Factory().NewNodeList([]*ast.Node{moduleParam}), nil, nil, moduleBody)
|
||||
moduleCall := tx.Factory().NewCallExpression(tx.Factory().NewParenthesizedExpression(moduleFunc), nil, nil, tx.Factory().NewNodeList([]*ast.Node{moduleArg}), ast.NodeFlagsNone)
|
||||
moduleStatement := tx.Factory().NewExpressionStatement(moduleCall)
|
||||
tx.EmitContext().SetOriginal(moduleStatement, node.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(moduleStatement, node.AsNode())
|
||||
tx.EmitContext().AddEmitFlags(moduleStatement, emitFlags)
|
||||
return tx.Factory().NewSyntaxList(append(statements, moduleStatement))
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) transformModuleBody(node *ast.ModuleDeclaration, namespaceLocalName *ast.IdentifierNode) *ast.BlockNode {
|
||||
savedCurrentNamespace := tx.currentNamespace
|
||||
savedCurrentScope := tx.currentScope
|
||||
savedCurrentScopeFirstDeclarationsOfName := tx.currentScopeFirstDeclarationsOfName
|
||||
|
||||
tx.currentNamespace = node.AsNode()
|
||||
tx.currentScopeFirstDeclarationsOfName = nil
|
||||
|
||||
var statements []*ast.Statement
|
||||
tx.EmitContext().StartVariableEnvironment()
|
||||
|
||||
var statementsLocation core.TextRange
|
||||
var blockLocation core.TextRange
|
||||
if node.Body != nil {
|
||||
if node.Body.Kind == ast.KindModuleBlock {
|
||||
// visit the children of `node` in advance to capture any references to namespace members
|
||||
node = tx.Visitor().VisitEachChild(node.AsNode()).AsModuleDeclaration()
|
||||
body := node.Body.AsModuleBlock()
|
||||
statements = body.Statements.Nodes
|
||||
statementsLocation = body.Statements.Loc
|
||||
blockLocation = body.Loc
|
||||
} else { // node.Body.Kind == ast.KindModuleDeclaration
|
||||
// !!! Strada didn't do this; why?
|
||||
// tx.currentScope = node.AsNode()
|
||||
statements, _ = tx.Visitor().VisitSlice([]*ast.Node{node.Body})
|
||||
moduleBlock := getInnermostModuleDeclarationFromDottedModule(node).Body.AsModuleBlock()
|
||||
statementsLocation = moduleBlock.Statements.Loc.WithPos(-1)
|
||||
}
|
||||
}
|
||||
|
||||
tx.currentNamespace = savedCurrentNamespace
|
||||
tx.currentScope = savedCurrentScope
|
||||
tx.currentScopeFirstDeclarationsOfName = savedCurrentScopeFirstDeclarationsOfName
|
||||
|
||||
statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements)
|
||||
statementList := tx.Factory().NewNodeList(statements)
|
||||
statementList.Loc = statementsLocation
|
||||
block := tx.Factory().NewBlock(statementList, true /*multiline*/)
|
||||
block.Loc = blockLocation
|
||||
|
||||
// namespace hello.hi.world {
|
||||
// function foo() {}
|
||||
//
|
||||
// // TODO, blah
|
||||
// }
|
||||
//
|
||||
// should be emitted as
|
||||
//
|
||||
// var hello;
|
||||
// (function (hello) {
|
||||
// var hi;
|
||||
// (function (hi) {
|
||||
// var world;
|
||||
// (function (world) {
|
||||
// function foo() { }
|
||||
// // TODO, blah
|
||||
// })(world = hi.world || (hi.world = {}));
|
||||
// })(hi = hello.hi || (hello.hi = {}));
|
||||
// })(hello || (hello = {}));
|
||||
//
|
||||
// We only want to emit comment on the namespace which contains block body itself, not the containing namespaces.
|
||||
if node.Body == nil || node.Body.Kind != ast.KindModuleBlock {
|
||||
tx.EmitContext().AddEmitFlags(block, printer.EFNoComments)
|
||||
}
|
||||
return block
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitImportEqualsDeclaration(node *ast.ImportEqualsDeclaration) *ast.Node {
|
||||
if node.ModuleReference.Kind == ast.KindExternalModuleReference {
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
moduleReference := tx.Factory().CreateExpressionFromEntityName(node.ModuleReference)
|
||||
tx.EmitContext().SetEmitFlags(moduleReference, printer.EFNoComments|printer.EFNoNestedComments)
|
||||
if !tx.isExportOfNamespace(node.AsNode()) {
|
||||
// export var ${name} = ${moduleReference};
|
||||
// var ${name} = ${moduleReference};
|
||||
varDecl := tx.Factory().NewVariableDeclaration(node.Name(), nil /*exclamationToken*/, nil /*type*/, moduleReference)
|
||||
tx.EmitContext().SetOriginal(varDecl, node.AsNode())
|
||||
varList := tx.Factory().NewVariableDeclarationList(tx.Factory().NewNodeList([]*ast.Node{varDecl}), ast.NodeFlagsNone)
|
||||
varModifiers := transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ast.ModifierFlagsExport)
|
||||
varStatement := tx.Factory().NewVariableStatement(varModifiers, varList)
|
||||
tx.EmitContext().SetOriginal(varStatement, node.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(varStatement, node.AsNode())
|
||||
return varStatement
|
||||
} else {
|
||||
// exports.${name} = ${moduleReference};
|
||||
statement := tx.createExportStatement(node.Name(), moduleReference, node.Loc, node.Loc, node.AsNode())
|
||||
statement.Loc = node.Loc
|
||||
return statement
|
||||
}
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitVariableStatement(node *ast.VariableStatement) *ast.Node {
|
||||
if tx.isExportOfNamespace(node.AsNode()) {
|
||||
expressions := []*ast.Expression{}
|
||||
for _, declaration := range node.DeclarationList.AsVariableDeclarationList().Declarations.Nodes {
|
||||
v := declaration.AsVariableDeclaration()
|
||||
if v.Initializer == nil {
|
||||
continue
|
||||
}
|
||||
if ast.IsBindingPattern(v.Name()) {
|
||||
expression := transformers.FlattenDestructuringAssignment(
|
||||
&tx.Transformer,
|
||||
tx.Visitor().VisitNode(declaration),
|
||||
false, /*needsValue*/
|
||||
transformers.FlattenLevelAll,
|
||||
tx.createNamespaceExportExpression,
|
||||
)
|
||||
if expression != nil {
|
||||
expressions = append(expressions, expression)
|
||||
}
|
||||
} else {
|
||||
expression := transformers.ConvertVariableDeclarationToAssignmentExpression(tx.EmitContext(), v)
|
||||
if expression != nil {
|
||||
expressions = append(expressions, expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(expressions) == 0 {
|
||||
return nil
|
||||
}
|
||||
expression := tx.Factory().InlineExpressions(expressions)
|
||||
statement := tx.Factory().NewExpressionStatement(expression)
|
||||
tx.EmitContext().SetOriginal(statement, node.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(statement, node.AsNode())
|
||||
|
||||
// re-visit as the new node
|
||||
savedCurrent := tx.currentNode
|
||||
tx.currentNode = statement
|
||||
statement = tx.Visitor().VisitEachChild(statement)
|
||||
tx.currentNode = savedCurrent
|
||||
return statement
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
// createNamespaceExportExpression creates an assignment to a namespace member for use as a
|
||||
// callback during destructuring flattening.
|
||||
func (tx *RuntimeSyntaxTransformer) createNamespaceExportExpression(exportName *ast.IdentifierNode, exportValue *ast.Expression, location *core.TextRange) *ast.Expression {
|
||||
memberName := tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(tx.currentNamespace), exportName)
|
||||
expression := tx.Factory().NewAssignmentExpression(memberName, exportValue)
|
||||
if location != nil {
|
||||
expression.Loc = *location
|
||||
}
|
||||
return expression
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitFunctionDeclaration(node *ast.FunctionDeclaration) *ast.Node {
|
||||
if tx.isExportOfNamespace(node.AsNode()) {
|
||||
updated := tx.Factory().UpdateFunctionDeclaration(
|
||||
node,
|
||||
tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExport)),
|
||||
node.AsteriskToken,
|
||||
tx.Visitor().VisitNode(node.Name()),
|
||||
nil, /*typeParameters*/
|
||||
tx.Visitor().VisitNodes(node.Parameters),
|
||||
nil, /*returnType*/
|
||||
nil, /*fullSignature*/
|
||||
tx.Visitor().VisitNode(node.Body),
|
||||
)
|
||||
export := tx.createExportStatementForDeclaration(node.AsNode())
|
||||
if export != nil {
|
||||
return tx.Factory().NewSyntaxList([]*ast.Node{updated, export})
|
||||
}
|
||||
return updated
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node.AsNode())
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) getParameterProperties(constructor *ast.Node) []*ast.ParameterDeclaration {
|
||||
var parameterProperties []*ast.ParameterDeclaration
|
||||
if constructor != nil {
|
||||
for _, parameter := range constructor.Parameters() {
|
||||
if ast.IsParameterPropertyDeclaration(parameter, constructor) {
|
||||
parameterProperties = append(parameterProperties, parameter.AsParameterDeclaration())
|
||||
}
|
||||
}
|
||||
}
|
||||
return parameterProperties
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitClassDeclaration(node *ast.ClassDeclaration) *ast.Node {
|
||||
exported := tx.isExportOfNamespace(node.AsNode())
|
||||
var modifiers *ast.ModifierList
|
||||
if exported {
|
||||
modifiers = tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault))
|
||||
} else {
|
||||
modifiers = tx.Visitor().VisitModifiers(node.Modifiers())
|
||||
}
|
||||
|
||||
name := tx.Visitor().VisitNode(node.Name())
|
||||
if name == nil && (exported || ast.ChildIsDecorated(tx.compilerOptions.ExperimentalDecorators.IsTrue(), node.AsNode(), nil)) {
|
||||
name = tx.Factory().NewGeneratedNameForNode(node.AsNode())
|
||||
}
|
||||
heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses)
|
||||
members := tx.Visitor().VisitNodes(node.Members)
|
||||
parameterProperties := tx.getParameterProperties(core.Find(node.Members.Nodes, ast.IsConstructorDeclaration))
|
||||
|
||||
if len(parameterProperties) > 0 {
|
||||
var newMembers []*ast.ClassElement
|
||||
for _, parameter := range parameterProperties {
|
||||
if ast.IsIdentifier(parameter.Name()) {
|
||||
parameterProperty := tx.Factory().NewPropertyDeclaration(
|
||||
nil, /*modifiers*/
|
||||
parameter.Name().Clone(tx.Factory()),
|
||||
nil, /*questionOrExclamationToken*/
|
||||
nil, /*type*/
|
||||
nil, /*initializer*/
|
||||
)
|
||||
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
|
||||
newMembers = append(newMembers, parameterProperty)
|
||||
}
|
||||
}
|
||||
if len(newMembers) > 0 {
|
||||
newMembers = append(newMembers, members.Nodes...)
|
||||
members = tx.Factory().NewNodeList(newMembers)
|
||||
members.Loc = node.Members.Loc
|
||||
}
|
||||
}
|
||||
|
||||
updated := tx.Factory().UpdateClassDeclaration(node, modifiers, name, nil /*typeParameters*/, heritageClauses, members)
|
||||
if exported {
|
||||
export := tx.createExportStatementForDeclaration(node.AsNode())
|
||||
if export != nil {
|
||||
return tx.Factory().NewSyntaxList([]*ast.Node{updated, export})
|
||||
}
|
||||
}
|
||||
return updated
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitClassExpression(node *ast.ClassExpression) *ast.Node {
|
||||
modifiers := tx.Visitor().VisitModifiers(transformers.ExtractModifiers(tx.EmitContext(), node.Modifiers(), ^ast.ModifierFlagsExportDefault))
|
||||
name := tx.Visitor().VisitNode(node.Name())
|
||||
heritageClauses := tx.Visitor().VisitNodes(node.HeritageClauses)
|
||||
members := tx.Visitor().VisitNodes(node.Members)
|
||||
parameterProperties := tx.getParameterProperties(core.Find(node.Members.Nodes, ast.IsConstructorDeclaration))
|
||||
|
||||
if len(parameterProperties) > 0 {
|
||||
var newMembers []*ast.ClassElement
|
||||
for _, parameter := range parameterProperties {
|
||||
if ast.IsIdentifier(parameter.Name()) {
|
||||
parameterProperty := tx.Factory().NewPropertyDeclaration(
|
||||
nil, /*modifiers*/
|
||||
parameter.Name().Clone(tx.Factory()),
|
||||
nil, /*questionOrExclamationToken*/
|
||||
nil, /*type*/
|
||||
nil, /*initializer*/
|
||||
)
|
||||
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
|
||||
newMembers = append(newMembers, parameterProperty)
|
||||
}
|
||||
}
|
||||
if len(newMembers) > 0 {
|
||||
newMembers = append(newMembers, members.Nodes...)
|
||||
members = tx.Factory().NewNodeList(newMembers)
|
||||
members.Loc = node.Members.Loc
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Factory().UpdateClassExpression(node, modifiers, name, nil /*typeParameters*/, heritageClauses, members)
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitConstructorDeclaration(node *ast.ConstructorDeclaration) *ast.Node {
|
||||
modifiers := tx.Visitor().VisitModifiers(node.Modifiers())
|
||||
parameters := tx.EmitContext().VisitParameters(node.ParameterList(), tx.Visitor())
|
||||
body := tx.visitConstructorBody(node.Body.AsBlock(), node.AsNode())
|
||||
return tx.Factory().UpdateConstructorDeclaration(node, modifiers, nil /*typeParameters*/, parameters, nil /*returnType*/, nil /*fullSignature*/, body)
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitConstructorBody(body *ast.Block, constructor *ast.Node) *ast.Node {
|
||||
parameterProperties := tx.getParameterProperties(constructor)
|
||||
if len(parameterProperties) == 0 {
|
||||
return tx.EmitContext().VisitFunctionBody(body.AsNode(), tx.Visitor())
|
||||
}
|
||||
|
||||
grandparentOfBody := tx.pushNode(body.AsNode())
|
||||
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(body.AsNode())
|
||||
|
||||
tx.EmitContext().StartVariableEnvironment()
|
||||
prologue, rest := tx.Factory().SplitStandardPrologue(body.Statements.Nodes)
|
||||
statements := slices.Clone(prologue)
|
||||
|
||||
// Transform parameters into property assignments. Transforms this:
|
||||
//
|
||||
// constructor (public x, public y) {
|
||||
// }
|
||||
//
|
||||
// Into this:
|
||||
//
|
||||
// constructor (x, y) {
|
||||
// this.x = x;
|
||||
// this.y = y;
|
||||
// }
|
||||
//
|
||||
|
||||
var parameterPropertyAssignments []*ast.Statement
|
||||
for _, parameter := range parameterProperties {
|
||||
if ast.IsIdentifier(parameter.Name()) {
|
||||
propertyName := parameter.Name().Clone(tx.Factory())
|
||||
propertyName.Parent = parameter.Name().Parent //nolint:customlint // .Parent set to get node to printback using text from original file instead of processed text; TODO: this should be achievable via EmitFlags instead
|
||||
tx.EmitContext().AddEmitFlags(propertyName, printer.EFNoComments|printer.EFNoSourceMap)
|
||||
|
||||
localName := parameter.Name().Clone(tx.Factory())
|
||||
localName.Parent = parameter.Name().Parent //nolint:customlint // .Parent set to get node to printback using text from original file instead of processed text; TODO: this should be achievable via EmitFlags instead
|
||||
tx.EmitContext().AddEmitFlags(localName, printer.EFNoComments)
|
||||
|
||||
parameterProperty := tx.Factory().NewExpressionStatement(
|
||||
tx.Factory().NewAssignmentExpression(
|
||||
tx.Factory().NewPropertyAccessExpression(
|
||||
tx.Factory().NewThisExpression(),
|
||||
nil, /*questionDotToken*/
|
||||
propertyName,
|
||||
ast.NodeFlagsNone,
|
||||
),
|
||||
localName,
|
||||
),
|
||||
)
|
||||
tx.EmitContext().SetOriginal(parameterProperty, parameter.AsNode())
|
||||
tx.EmitContext().AddEmitFlags(parameterProperty, printer.EFStartOnNewLine)
|
||||
parameterPropertyAssignments = append(parameterPropertyAssignments, parameterProperty)
|
||||
}
|
||||
}
|
||||
|
||||
superPath := transformers.FindSuperStatementIndexPath(rest, 0)
|
||||
|
||||
if len(superPath) > 0 {
|
||||
statements = append(statements, tx.transformConstructorBodyWorker(rest, superPath, parameterPropertyAssignments)...)
|
||||
} else {
|
||||
statements = append(statements, parameterPropertyAssignments...)
|
||||
statements = append(statements, core.FirstResult(tx.Visitor().VisitSlice(rest))...)
|
||||
}
|
||||
|
||||
statements = tx.EmitContext().EndAndMergeVariableEnvironment(statements)
|
||||
statementList := tx.Factory().NewNodeList(statements)
|
||||
statementList.Loc = body.Statements.Loc
|
||||
|
||||
tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
|
||||
tx.popNode(grandparentOfBody)
|
||||
updated := tx.Factory().NewBlock(statementList /*multiline*/, true)
|
||||
tx.EmitContext().SetOriginal(updated, body.AsNode())
|
||||
updated.Loc = body.Loc
|
||||
return updated
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) transformConstructorBodyWorker(statementsIn []*ast.Statement, superPath []int, initializerStatements []*ast.Statement) []*ast.Statement {
|
||||
var statementsOut []*ast.Statement
|
||||
superStatementIndex := superPath[0]
|
||||
superStatement := statementsIn[superStatementIndex]
|
||||
|
||||
// visit up to the statement containing `super`
|
||||
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[:superStatementIndex]))...)
|
||||
|
||||
// if the statement containing `super` is a `try` statement, transform the body of the `try` block
|
||||
if ast.IsTryStatement(superStatement) {
|
||||
tryStatement := superStatement.AsTryStatement()
|
||||
tryBlock := tryStatement.TryBlock.AsBlock()
|
||||
|
||||
// keep track of hierarchy as we descend
|
||||
grandparentOfTryStatement := tx.pushNode(tryStatement.AsNode())
|
||||
grandparentOfTryBlock := tx.pushNode(tryBlock.AsNode())
|
||||
savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName := tx.pushScope(tryBlock.AsNode())
|
||||
|
||||
// visit the `try` block
|
||||
tryBlockStatements := tx.transformConstructorBodyWorker(
|
||||
tryBlock.Statements.Nodes,
|
||||
superPath[1:],
|
||||
initializerStatements,
|
||||
)
|
||||
|
||||
// restore hierarchy as we ascend to the `try` statement
|
||||
tx.popScope(savedCurrentScope, savedCurrentScopeFirstDeclarationsOfName)
|
||||
tx.popNode(grandparentOfTryBlock)
|
||||
|
||||
tryBlockStatementList := tx.Factory().NewNodeList(tryBlockStatements)
|
||||
tryBlockStatementList.Loc = tryBlock.Statements.Loc
|
||||
statementsOut = append(statementsOut, tx.Factory().UpdateTryStatement(
|
||||
tryStatement,
|
||||
tx.Factory().UpdateBlock(tryBlock, tryBlockStatementList, tryBlock.MultiLine),
|
||||
tx.Visitor().VisitNode(tryStatement.CatchClause),
|
||||
tx.Visitor().VisitNode(tryStatement.FinallyBlock),
|
||||
))
|
||||
|
||||
// restore hierarchy as we ascend to the parent of the `try` statement
|
||||
tx.popNode(grandparentOfTryStatement)
|
||||
} else {
|
||||
// visit the statement containing `super`
|
||||
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[superStatementIndex:superStatementIndex+1]))...)
|
||||
|
||||
// insert the initializer statements
|
||||
statementsOut = append(statementsOut, initializerStatements...)
|
||||
}
|
||||
|
||||
// visit the statements after `super`
|
||||
statementsOut = append(statementsOut, core.FirstResult(tx.Visitor().VisitSlice(statementsIn[superStatementIndex+1:]))...)
|
||||
return statementsOut
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitShorthandPropertyAssignment(node *ast.ShorthandPropertyAssignment) *ast.Node {
|
||||
name := node.Name()
|
||||
exportedOrImportedName := tx.visitExpressionIdentifier(name)
|
||||
if exportedOrImportedName != name {
|
||||
expression := exportedOrImportedName
|
||||
if node.ObjectAssignmentInitializer != nil {
|
||||
equalsToken := node.EqualsToken
|
||||
if equalsToken == nil {
|
||||
equalsToken = tx.Factory().NewToken(ast.KindEqualsToken)
|
||||
}
|
||||
expression = tx.Factory().NewBinaryExpression(
|
||||
nil, /*modifiers*/
|
||||
expression,
|
||||
nil, /*typeNode*/
|
||||
equalsToken,
|
||||
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
||||
)
|
||||
}
|
||||
|
||||
updated := tx.Factory().NewPropertyAssignment(nil /*modifiers*/, node.Name(), nil /*postfixToken*/, nil /*typeNode*/, expression)
|
||||
updated.Loc = node.Loc
|
||||
tx.EmitContext().SetOriginal(updated, node.AsNode())
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(updated, node.AsNode())
|
||||
return updated
|
||||
}
|
||||
return tx.Factory().UpdateShorthandPropertyAssignment(
|
||||
node,
|
||||
nil, /*modifiers*/
|
||||
exportedOrImportedName,
|
||||
nil, /*postfixToken*/
|
||||
nil, /*typeNode*/
|
||||
node.EqualsToken,
|
||||
tx.Visitor().VisitNode(node.ObjectAssignmentInitializer),
|
||||
)
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitIdentifier(node *ast.IdentifierNode) *ast.Node {
|
||||
if transformers.IsIdentifierReference(node, tx.parentNode) {
|
||||
return tx.visitExpressionIdentifier(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) visitExpressionIdentifier(node *ast.IdentifierNode) *ast.Node {
|
||||
if (tx.currentEnum != nil || tx.currentNamespace != nil) && !transformers.IsGeneratedIdentifier(tx.EmitContext(), node) && !transformers.IsLocalName(tx.EmitContext(), node) {
|
||||
location := tx.EmitContext().MostOriginal(node.AsNode())
|
||||
container := tx.resolver.GetReferencedExportContainer(location, false /*prefixLocals*/)
|
||||
if container != nil && (ast.IsEnumDeclaration(container) || ast.IsModuleDeclaration(container)) {
|
||||
containerName := tx.getNamespaceContainerName(container)
|
||||
|
||||
memberName := node.Clone(tx.Factory())
|
||||
tx.EmitContext().SetEmitFlags(memberName, printer.EFNoComments|printer.EFNoSourceMap)
|
||||
|
||||
expression := tx.Factory().GetNamespaceMemberName(containerName, memberName, printer.NameOptions{AllowSourceMaps: true})
|
||||
tx.EmitContext().AssignCommentAndSourceMapRanges(expression, node.AsNode())
|
||||
return expression
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) createExportStatementForDeclaration(node *ast.Declaration) *ast.Statement {
|
||||
exportName := tx.Factory().GetExternalModuleOrNamespaceExportName(tx.getNamespaceContainerName(tx.currentNamespace), node, false /*allowComments*/, true /*allowSourceMaps*/)
|
||||
localName := tx.Factory().GetLocalName(node)
|
||||
expression := tx.Factory().NewAssignmentExpression(exportName, localName)
|
||||
exportAssignmentSourceMapRange := node.Loc
|
||||
if node.Name() != nil {
|
||||
exportAssignmentSourceMapRange = exportAssignmentSourceMapRange.WithPos(node.Name().Pos())
|
||||
}
|
||||
tx.EmitContext().SetSourceMapRange(expression, exportAssignmentSourceMapRange)
|
||||
|
||||
statement := tx.Factory().NewExpressionStatement(expression)
|
||||
exportStatementSourceMapRange := node.Loc.WithPos(-1)
|
||||
tx.EmitContext().SetSourceMapRange(statement, exportStatementSourceMapRange)
|
||||
return statement
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) createExportAssignment(name *ast.IdentifierNode, expression *ast.Expression, exportAssignmentSourceMapRange core.TextRange, original *ast.Node) *ast.Expression {
|
||||
exportName := tx.getNamespaceQualifiedProperty(tx.getNamespaceContainerName(tx.currentNamespace), name)
|
||||
exportAssignment := tx.Factory().NewAssignmentExpression(exportName, expression)
|
||||
tx.EmitContext().SetOriginal(exportAssignment, original)
|
||||
tx.EmitContext().SetSourceMapRange(exportAssignment, exportAssignmentSourceMapRange)
|
||||
return exportAssignment
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) createExportStatement(name *ast.IdentifierNode, expression *ast.Expression, exportAssignmentSourceMapRange core.TextRange, exportStatementSourceMapRange core.TextRange, original *ast.Node) *ast.Statement {
|
||||
exportStatement := tx.Factory().NewExpressionStatement(tx.createExportAssignment(name, expression, exportAssignmentSourceMapRange, original))
|
||||
tx.EmitContext().SetOriginal(exportStatement, original)
|
||||
tx.EmitContext().SetSourceMapRange(exportStatement, exportStatementSourceMapRange)
|
||||
return exportStatement
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) shouldEmitEnumDeclaration(node *ast.EnumDeclaration) bool {
|
||||
return !ast.IsEnumConst(node.AsNode()) || tx.compilerOptions.ShouldPreserveConstEnums()
|
||||
}
|
||||
|
||||
func (tx *RuntimeSyntaxTransformer) shouldEmitModuleDeclaration(node *ast.ModuleDeclaration) bool {
|
||||
pn := tx.EmitContext().ParseNode(node.AsNode())
|
||||
if pn == nil {
|
||||
// If we can't find a parse tree node, assume the node is instantiated.
|
||||
return true
|
||||
}
|
||||
return ast.IsInstantiatedModule(pn, tx.compilerOptions.ShouldPreserveConstEnums())
|
||||
}
|
||||
|
||||
func getInnermostModuleDeclarationFromDottedModule(moduleDeclaration *ast.ModuleDeclaration) *ast.ModuleDeclaration {
|
||||
for moduleDeclaration.Body != nil && moduleDeclaration.Body.Kind == ast.KindModuleDeclaration {
|
||||
moduleDeclaration = moduleDeclaration.Body.AsModuleDeclaration()
|
||||
}
|
||||
return moduleDeclaration
|
||||
}
|
||||
393
tools/tsgo/internal/transformers/tstransforms/typeeraser.go
Normal file
393
tools/tsgo/internal/transformers/tstransforms/typeeraser.go
Normal file
@@ -0,0 +1,393 @@
|
||||
package tstransforms
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
)
|
||||
|
||||
type TypeEraserTransformer struct {
|
||||
transformers.Transformer
|
||||
compilerOptions *core.CompilerOptions
|
||||
parentNode *ast.Node
|
||||
currentNode *ast.Node
|
||||
}
|
||||
|
||||
func NewTypeEraserTransformer(opt *transformers.TransformOptions) *transformers.Transformer {
|
||||
compilerOptions := opt.CompilerOptions
|
||||
emitContext := opt.Context
|
||||
tx := &TypeEraserTransformer{compilerOptions: compilerOptions}
|
||||
return tx.NewTransformer(tx.visit, emitContext)
|
||||
}
|
||||
|
||||
// Pushes a new child node onto the ancestor tracking stack, returning the grandparent node to be restored later via `popNode`.
|
||||
func (tx *TypeEraserTransformer) pushNode(node *ast.Node) (grandparentNode *ast.Node) {
|
||||
grandparentNode = tx.parentNode
|
||||
tx.parentNode = tx.currentNode
|
||||
tx.currentNode = node
|
||||
return grandparentNode
|
||||
}
|
||||
|
||||
// Pops the last child node off the ancestor tracking stack, restoring the grandparent node.
|
||||
func (tx *TypeEraserTransformer) popNode(grandparentNode *ast.Node) {
|
||||
tx.currentNode = tx.parentNode
|
||||
tx.parentNode = grandparentNode
|
||||
}
|
||||
|
||||
func (tx *TypeEraserTransformer) elide(node *ast.Statement) *ast.Statement {
|
||||
return tx.EmitContext().NewNotEmittedStatement(node.AsNode())
|
||||
}
|
||||
|
||||
func (tx *TypeEraserTransformer) visit(node *ast.Node) *ast.Node {
|
||||
if node.SubtreeFacts()&ast.SubtreeContainsTypeScript == 0 {
|
||||
return node
|
||||
}
|
||||
|
||||
if ast.IsStatement(node) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient) {
|
||||
return tx.elide(node)
|
||||
}
|
||||
|
||||
grandparentNode := tx.pushNode(node)
|
||||
defer tx.popNode(grandparentNode)
|
||||
|
||||
switch node.Kind {
|
||||
case
|
||||
// TypeScript accessibility and readonly modifiers are elided
|
||||
ast.KindPublicKeyword,
|
||||
ast.KindPrivateKeyword,
|
||||
ast.KindProtectedKeyword,
|
||||
ast.KindAbstractKeyword,
|
||||
ast.KindOverrideKeyword,
|
||||
ast.KindConstKeyword,
|
||||
ast.KindDeclareKeyword,
|
||||
ast.KindReadonlyKeyword,
|
||||
// TypeScript type nodes are elided.
|
||||
ast.KindArrayType,
|
||||
ast.KindTupleType,
|
||||
ast.KindOptionalType,
|
||||
ast.KindRestType,
|
||||
ast.KindTypeLiteral,
|
||||
ast.KindTypePredicate,
|
||||
ast.KindTypeParameter,
|
||||
ast.KindAnyKeyword,
|
||||
ast.KindUnknownKeyword,
|
||||
ast.KindBooleanKeyword,
|
||||
ast.KindStringKeyword,
|
||||
ast.KindNumberKeyword,
|
||||
ast.KindNeverKeyword,
|
||||
ast.KindVoidKeyword,
|
||||
ast.KindSymbolKeyword,
|
||||
ast.KindConstructorType,
|
||||
ast.KindFunctionType,
|
||||
ast.KindTypeQuery,
|
||||
ast.KindTypeReference,
|
||||
ast.KindUnionType,
|
||||
ast.KindIntersectionType,
|
||||
ast.KindConditionalType,
|
||||
ast.KindParenthesizedType,
|
||||
ast.KindThisType,
|
||||
ast.KindTypeOperator,
|
||||
ast.KindIndexedAccessType,
|
||||
ast.KindMappedType,
|
||||
ast.KindLiteralType,
|
||||
// TypeScript index signatures are elided.
|
||||
ast.KindIndexSignature:
|
||||
return nil
|
||||
|
||||
case ast.KindInKeyword, ast.KindOutKeyword:
|
||||
// TypeScript `in`/`out` variance modifiers are elided. These keywords are only
|
||||
// meaningful as modifiers on type parameters (which are themselves elided), but they may
|
||||
// appear as a grammar error on other declarations and must not leak into the emitted JS.
|
||||
// The `in` binary operator shares this token kind, so only elide when used as a modifier.
|
||||
if tx.parentNode == nil || !ast.IsBinaryExpression(tx.parentNode) {
|
||||
return nil
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
|
||||
case ast.KindJSImportDeclaration:
|
||||
// reparsed commonjs are elided
|
||||
return nil
|
||||
case ast.KindTypeAliasDeclaration,
|
||||
ast.KindJSTypeAliasDeclaration,
|
||||
ast.KindInterfaceDeclaration:
|
||||
// TypeScript type-only declarations are elided.
|
||||
return tx.elide(node)
|
||||
|
||||
case ast.KindNamespaceExportDeclaration:
|
||||
// TypeScript namespace export declarations are elided.
|
||||
return nil
|
||||
|
||||
case ast.KindModuleDeclaration:
|
||||
if !ast.IsIdentifier(node.Name()) ||
|
||||
!ast.IsInstantiatedModule(node, tx.compilerOptions.ShouldPreserveConstEnums()) ||
|
||||
getInnermostModuleDeclarationFromDottedModule(node.AsModuleDeclaration()).Body == nil {
|
||||
// TypeScript module declarations are elided if they are not instantiated or have no body
|
||||
return tx.elide(node)
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
|
||||
case ast.KindExpressionWithTypeArguments:
|
||||
n := node.AsExpressionWithTypeArguments()
|
||||
return tx.Factory().UpdateExpressionWithTypeArguments(n, tx.Visitor().VisitNode(n.Expression), nil)
|
||||
|
||||
case ast.KindPropertyDeclaration:
|
||||
if tx.compilerOptions.ExperimentalDecorators.IsTrue() && ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient|ast.ModifierFlagsAbstract) && ast.HasDecorators(node) {
|
||||
// declare/abstract props with decorators must be preserved until the decorator transform can process them and remove them
|
||||
n := node.AsPropertyDeclaration()
|
||||
return tx.Factory().UpdatePropertyDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
|
||||
}
|
||||
if ast.HasSyntacticModifier(node, ast.ModifierFlagsAmbient|ast.ModifierFlagsAbstract) {
|
||||
// TypeScript `declare` fields are elided
|
||||
return nil
|
||||
}
|
||||
n := node.AsPropertyDeclaration()
|
||||
return tx.Factory().UpdatePropertyDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
|
||||
|
||||
case ast.KindConstructor:
|
||||
n := node.AsConstructorDeclaration()
|
||||
if ast.NodeIsMissing(n.Body) {
|
||||
// TypeScript overloads are elided
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateConstructorDeclaration(n, nil, nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
|
||||
|
||||
case ast.KindMethodDeclaration:
|
||||
n := node.AsMethodDeclaration()
|
||||
if ast.NodeIsMissing(n.Body) {
|
||||
// TypeScript overloads are elided
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateMethodDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
|
||||
|
||||
case ast.KindGetAccessor:
|
||||
n := node.AsGetAccessorDeclaration()
|
||||
if ast.NodeIsMissing(n.Body) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
|
||||
// Abstract accessors are elided
|
||||
return nil
|
||||
}
|
||||
body := tx.Visitor().VisitNode(n.Body)
|
||||
if body == nil {
|
||||
body = tx.Factory().NewBlock(tx.Factory().NewNodeList(nil), false)
|
||||
}
|
||||
return tx.Factory().UpdateGetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, body)
|
||||
|
||||
case ast.KindSetAccessor:
|
||||
n := node.AsSetAccessorDeclaration()
|
||||
if ast.NodeIsMissing(n.Body) && ast.HasSyntacticModifier(node, ast.ModifierFlagsAbstract) {
|
||||
// Abstract accessors are elided
|
||||
return nil
|
||||
}
|
||||
body := tx.Visitor().VisitNode(n.Body)
|
||||
if body == nil {
|
||||
body = tx.Factory().NewBlock(tx.Factory().NewNodeList(nil), false)
|
||||
}
|
||||
return tx.Factory().UpdateSetAccessorDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, body)
|
||||
|
||||
case ast.KindVariableDeclaration:
|
||||
n := node.AsVariableDeclaration()
|
||||
updated := tx.Factory().UpdateVariableDeclaration(n, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
|
||||
if n.Type != nil {
|
||||
tx.EmitContext().SetTypeNode(updated.AsVariableDeclaration().Name(), n.Type)
|
||||
}
|
||||
return updated
|
||||
|
||||
case ast.KindHeritageClause:
|
||||
n := node.AsHeritageClause()
|
||||
if n.Token == ast.KindImplementsKeyword {
|
||||
// TypeScript `implements` clauses are elided
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateHeritageClause(n, n.Token, tx.Visitor().VisitNodes(n.Types))
|
||||
|
||||
case ast.KindClassDeclaration:
|
||||
n := node.AsClassDeclaration()
|
||||
return tx.Factory().UpdateClassDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.HeritageClauses), tx.Visitor().VisitNodes(n.Members))
|
||||
|
||||
case ast.KindClassExpression:
|
||||
n := node.AsClassExpression()
|
||||
return tx.Factory().UpdateClassExpression(n, tx.Visitor().VisitModifiers(n.Modifiers()), tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.HeritageClauses), tx.Visitor().VisitNodes(n.Members))
|
||||
|
||||
case ast.KindFunctionDeclaration:
|
||||
n := node.AsFunctionDeclaration()
|
||||
if ast.NodeIsMissing(n.Body) {
|
||||
// TypeScript overloads are elided
|
||||
return tx.elide(node)
|
||||
}
|
||||
return tx.Factory().UpdateFunctionDeclaration(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
|
||||
|
||||
case ast.KindFunctionExpression:
|
||||
n := node.AsFunctionExpression()
|
||||
return tx.Factory().UpdateFunctionExpression(n, tx.Visitor().VisitModifiers(n.Modifiers()), n.AsteriskToken, tx.Visitor().VisitNode(n.Name()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, tx.Visitor().VisitNode(n.Body))
|
||||
|
||||
case ast.KindArrowFunction:
|
||||
n := node.AsArrowFunction()
|
||||
return tx.Factory().UpdateArrowFunction(n, tx.Visitor().VisitModifiers(n.Modifiers()), nil, tx.Visitor().VisitNodes(n.Parameters), nil, nil, n.EqualsGreaterThanToken, tx.Visitor().VisitNode(n.Body))
|
||||
|
||||
case ast.KindParameter:
|
||||
if ast.IsThisParameter(node) {
|
||||
// TypeScript `this` parameters are elided
|
||||
return nil
|
||||
}
|
||||
n := node.AsParameterDeclaration()
|
||||
// preserve parameter property modifiers to be handled by the runtime transformer
|
||||
var modifiers *ast.ModifierList
|
||||
if ast.IsParameterPropertyDeclaration(node, tx.parentNode) {
|
||||
modifiers = transformers.ExtractModifiers(tx.EmitContext(), n.Modifiers(), ast.ModifierFlagsParameterPropertyModifier)
|
||||
}
|
||||
// preserve decorators for the decorator transforms
|
||||
if ast.HasDecorators(node) {
|
||||
decorators := node.Decorators()
|
||||
visited, _ := tx.Visitor().VisitSlice(decorators)
|
||||
if modifiers == nil {
|
||||
modifiers = tx.Factory().NewModifierList(visited)
|
||||
} else {
|
||||
modifiers = tx.Factory().NewModifierList(slices.Concat(modifiers.Nodes, visited))
|
||||
}
|
||||
}
|
||||
return tx.Factory().UpdateParameterDeclaration(n, modifiers, n.DotDotDotToken, tx.Visitor().VisitNode(n.Name()), nil, nil, tx.Visitor().VisitNode(n.Initializer))
|
||||
|
||||
case ast.KindCallExpression:
|
||||
n := node.AsCallExpression()
|
||||
return tx.Factory().UpdateCallExpression(n, tx.Visitor().VisitNode(n.Expression), n.QuestionDotToken, nil, tx.Visitor().VisitNodes(n.Arguments), n.Flags)
|
||||
|
||||
case ast.KindNewExpression:
|
||||
n := node.AsNewExpression()
|
||||
return tx.Factory().UpdateNewExpression(n, tx.Visitor().VisitNode(n.Expression), nil, tx.Visitor().VisitNodes(n.Arguments))
|
||||
|
||||
case ast.KindTaggedTemplateExpression:
|
||||
n := node.AsTaggedTemplateExpression()
|
||||
return tx.Factory().UpdateTaggedTemplateExpression(n, tx.Visitor().VisitNode(n.Tag), n.QuestionDotToken, nil, tx.Visitor().VisitNode(n.Template), n.Flags)
|
||||
|
||||
case ast.KindNonNullExpression, ast.KindTypeAssertionExpression, ast.KindAsExpression, ast.KindSatisfiesExpression:
|
||||
partial := tx.Factory().NewPartiallyEmittedExpression(tx.Visitor().VisitNode(node.Expression()))
|
||||
tx.EmitContext().SetOriginal(partial, node)
|
||||
partial.Loc = node.Loc
|
||||
return partial
|
||||
|
||||
case ast.KindParenthesizedExpression:
|
||||
if !ast.IsJSDocTypeAssertion(node) {
|
||||
n := node.AsParenthesizedExpression()
|
||||
expression := ast.SkipOuterExpressions(n.Expression, ast.OEKAllExceptAssertionsOrExpressionsWithTypeArguments)
|
||||
if ast.IsAssertionExpression(expression) || ast.IsSatisfiesExpression(expression) {
|
||||
partial := tx.Factory().NewPartiallyEmittedExpression(tx.Visitor().VisitNode(n.Expression))
|
||||
tx.EmitContext().SetOriginal(partial, node)
|
||||
partial.Loc = node.Loc
|
||||
return partial
|
||||
}
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
|
||||
case ast.KindJsxSelfClosingElement:
|
||||
n := node.AsJsxSelfClosingElement()
|
||||
return tx.Factory().UpdateJsxSelfClosingElement(n, tx.Visitor().VisitNode(n.TagName), nil, tx.Visitor().VisitNode(n.Attributes))
|
||||
|
||||
case ast.KindJsxOpeningElement:
|
||||
n := node.AsJsxOpeningElement()
|
||||
return tx.Factory().UpdateJsxOpeningElement(n, tx.Visitor().VisitNode(n.TagName), nil, tx.Visitor().VisitNode(n.Attributes))
|
||||
|
||||
case ast.KindImportEqualsDeclaration:
|
||||
n := node.AsImportEqualsDeclaration()
|
||||
if n.IsTypeOnly {
|
||||
// elide type-only imports
|
||||
return nil
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
|
||||
case ast.KindImportDeclaration:
|
||||
n := node.AsImportDeclaration()
|
||||
if n.ImportClause == nil {
|
||||
// Do not elide a side-effect only import declaration.
|
||||
// import "foo";
|
||||
return node
|
||||
}
|
||||
importClause := tx.Visitor().VisitNode(n.ImportClause)
|
||||
if importClause == nil {
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateImportDeclaration(n, n.Modifiers(), importClause, n.ModuleSpecifier, n.Attributes)
|
||||
|
||||
case ast.KindImportClause:
|
||||
n := node.AsImportClause()
|
||||
if n.IsTypeOnly() {
|
||||
// Always elide type-only imports
|
||||
return nil
|
||||
}
|
||||
name := n.Name()
|
||||
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.KindNamedImports:
|
||||
n := node.AsNamedImports()
|
||||
if len(n.Elements.Nodes) == 0 {
|
||||
// Do not elide a side-effect only import declaration.
|
||||
return node
|
||||
}
|
||||
elements := tx.Visitor().VisitNodes(n.Elements)
|
||||
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && len(elements.Nodes) == 0 {
|
||||
// all import specifiers were elided
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateNamedImports(n, elements)
|
||||
|
||||
case ast.KindImportSpecifier:
|
||||
n := node.AsImportSpecifier()
|
||||
if n.IsTypeOnly {
|
||||
// elide type-only or unused imports
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
|
||||
case ast.KindExportDeclaration:
|
||||
n := node.AsExportDeclaration()
|
||||
if n.IsTypeOnly {
|
||||
// elide type-only exports
|
||||
return nil
|
||||
}
|
||||
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()
|
||||
if len(n.Elements.Nodes) == 0 {
|
||||
// Do not elide an empty export declaration.
|
||||
return node
|
||||
}
|
||||
|
||||
elements := tx.Visitor().VisitNodes(n.Elements)
|
||||
if !tx.compilerOptions.VerbatimModuleSyntax.IsTrue() && len(elements.Nodes) == 0 {
|
||||
// all export specifiers were elided
|
||||
return nil
|
||||
}
|
||||
return tx.Factory().UpdateNamedExports(n, elements)
|
||||
|
||||
case ast.KindExportSpecifier:
|
||||
n := node.AsExportSpecifier()
|
||||
if n.IsTypeOnly {
|
||||
// elide unused export
|
||||
return nil
|
||||
}
|
||||
return node
|
||||
|
||||
case ast.KindEnumDeclaration:
|
||||
if ast.IsEnumConst(node) {
|
||||
return node
|
||||
}
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
|
||||
default:
|
||||
return tx.Visitor().VisitEachChild(node)
|
||||
}
|
||||
}
|
||||
106
tools/tsgo/internal/transformers/tstransforms/typeeraser_test.go
Normal file
106
tools/tsgo/internal/transformers/tstransforms/typeeraser_test.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package tstransforms_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
|
||||
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
|
||||
)
|
||||
|
||||
func TestTypeEraser(t *testing.T) {
|
||||
t.Parallel()
|
||||
data := []struct {
|
||||
title string
|
||||
input string
|
||||
output string
|
||||
jsx bool
|
||||
vms bool
|
||||
}{
|
||||
{title: "Modifiers", input: "class C { public x; private y }", output: "class C {\n x;\n y;\n}"},
|
||||
{title: "InterfaceDeclaration", input: "interface I { }", output: ""},
|
||||
{title: "TypeAliasDeclaration", input: "type T = U;", output: ""},
|
||||
{title: "NamespaceExportDeclaration", input: "export as namespace N;", output: ""},
|
||||
{title: "UninstantiatedNamespace1", input: "namespace N {}", output: ""},
|
||||
{title: "UninstantiatedNamespace2", input: "namespace N { export interface I {} }", output: ""},
|
||||
{title: "UninstantiatedNamespace3", input: "namespace N { export type T = U; }", output: ""},
|
||||
{title: "ExpressionWithTypeArguments", input: "F<T>", output: "F;"},
|
||||
{title: "PropertyDeclaration1", input: "class C { declare x; }", output: "class C {\n}"},
|
||||
{title: "PropertyDeclaration2", input: "class C { public x: number; }", output: "class C {\n x;\n}"},
|
||||
{title: "PropertyDeclaration3", input: "class C { public static x: number; }", output: "class C {\n static x;\n}"},
|
||||
{title: "ConstructorDeclaration1", input: "class C { constructor(); }", output: "class C {\n}"},
|
||||
{title: "ConstructorDeclaration2", input: "class C { public constructor() {} }", output: "class C {\n constructor() { }\n}"},
|
||||
{title: "MethodDeclaration1", input: "class C { m(); }", output: "class C {\n}"},
|
||||
{title: "MethodDeclaration2", input: "class C { public m<T>(): U {} }", output: "class C {\n m() { }\n}"},
|
||||
{title: "MethodDeclaration3", input: "class C { public static m<T>(): U {} }", output: "class C {\n static m() { }\n}"},
|
||||
{title: "GetAccessorDeclaration1", input: "class C { get m(); }", output: "class C {\n get m() { }\n}"},
|
||||
{title: "GetAccessorDeclaration2", input: "class C { public get m<T>(): U {} }", output: "class C {\n get m() { }\n}"},
|
||||
{title: "GetAccessorDeclaration3", input: "class C { public static get m<T>(): U {} }", output: "class C {\n static get m() { }\n}"},
|
||||
{title: "SetAccessorDeclaration1", input: "class C { set m(v); }", output: "class C {\n set m(v) { }\n}"},
|
||||
{title: "SetAccessorDeclaration2", input: "class C { public set m<T>(v): U {} }", output: "class C {\n set m(v) { }\n}"},
|
||||
{title: "SetAccessorDeclaration3", input: "class C { public static set m<T>(v): U {} }", output: "class C {\n static set m(v) { }\n}"},
|
||||
{title: "IndexSignature", input: "class C { [key: string]: number; }", output: "class C {\n}"},
|
||||
{title: "VariableDeclaration1", input: "declare var a;", output: ""},
|
||||
{title: "VariableDeclaration2", input: "var a: number", output: "var a;"},
|
||||
{title: "HeritageClause", input: "class C implements I {}", output: "class C {\n}"},
|
||||
{title: "ClassDeclaration1", input: "declare class C {}", output: ""},
|
||||
{title: "ClassDeclaration2", input: "class C<T> {}", output: "class C {\n}"},
|
||||
{title: "ClassExpression", input: "(class C<T> {})", output: "(class C {\n});"},
|
||||
{title: "FunctionDeclaration1", input: "declare function f() {}", output: ""},
|
||||
{title: "FunctionDeclaration2", input: "function f();", output: ""},
|
||||
{title: "FunctionDeclaration3", input: "function f<T>(): U {}", output: "function f() { }"},
|
||||
{title: "FunctionExpression", input: "(function f<T>(): U {})", output: "(function f() { });"},
|
||||
{title: "ArrowFunction", input: "(<T>(): U => {})", output: "(() => { });"},
|
||||
{title: "ParameterDeclaration", input: "function f(this: x, a: number, b?: boolean) {}", output: "function f(a, b) { }"},
|
||||
{title: "CallExpression", input: "f<T>()", output: "f();"},
|
||||
{title: "NewExpression1", input: "new f<T>()", output: "new f();"},
|
||||
{title: "NewExpression2", input: "new f<T>", output: "new f;"},
|
||||
{title: "TaggedTemplateExpression", input: "f<T>``", output: "f ``;"},
|
||||
{title: "NonNullExpression", input: "x!", output: "x;"},
|
||||
{title: "TypeAssertionExpression#1", input: "<T>x", output: "x;"},
|
||||
{title: "TypeAssertionExpression#2", input: "(<T>x).c", output: "x.c;"},
|
||||
{title: "AsExpression#1", input: "x as T", output: "x;"},
|
||||
{title: "AsExpression#2", input: "(x as T).c", output: "x.c;"},
|
||||
{title: "SatisfiesExpression#1", input: "x satisfies T", output: "x;"},
|
||||
{title: "SatisfiesExpression#2", input: "(x satisfies T).c", output: "x.c;"},
|
||||
{title: "JsxSelfClosingElement", input: "<x<T> />", output: "<x />;", jsx: true},
|
||||
{title: "JsxOpeningElement", input: "<x<T>></x>", output: "<x></x>;", jsx: true},
|
||||
{title: "ImportEqualsDeclaration#1", input: "import x = require(\"m\");", output: "import x = require(\"m\");"},
|
||||
{title: "ImportEqualsDeclaration#2", input: "import type x = require(\"m\");", output: ""},
|
||||
{title: "ImportEqualsDeclaration#3", input: "import x = y;", output: "import x = y;"},
|
||||
{title: "ImportEqualsDeclaration#4", input: "import type x = y;", output: ""},
|
||||
{title: "ImportDeclaration#1", input: "import \"m\";", output: "import \"m\";"},
|
||||
{title: "ImportDeclaration#2", input: "import * as x from \"m\"; x;", output: "import * as x from \"m\";\nx;"},
|
||||
{title: "ImportDeclaration#3", input: "import x from \"m\"; x;", output: "import x from \"m\";\nx;"},
|
||||
{title: "ImportDeclaration#4", input: "import { x } from \"m\"; x;", output: "import { x } from \"m\";\nx;"},
|
||||
{title: "ImportDeclaration#5", input: "import type * as x from \"m\";", output: ""},
|
||||
{title: "ImportDeclaration#6", input: "import type x from \"m\";", output: ""},
|
||||
{title: "ImportDeclaration#7", input: "import type { x } from \"m\";", output: ""},
|
||||
{title: "ImportDeclaration#8", input: "import { type x } from \"m\";", output: ""},
|
||||
{title: "ImportDeclaration#9", input: "import { type x } from \"m\";", output: "import {} from \"m\";", vms: true},
|
||||
{title: "ExportDeclaration#1", input: "export * from \"m\";", output: "export * from \"m\";"},
|
||||
{title: "ExportDeclaration#2", input: "export * as x from \"m\";", output: "export * as x from \"m\";"},
|
||||
{title: "ExportDeclaration#3", input: "export { x } from \"m\";", output: "export { x } from \"m\";"},
|
||||
{title: "ExportDeclaration#4", input: "export type * from \"m\";", output: ""},
|
||||
{title: "ExportDeclaration#5", input: "export type * as x from \"m\";", output: ""},
|
||||
{title: "ExportDeclaration#6", input: "export type { x } from \"m\";", output: ""},
|
||||
{title: "ExportDeclaration#7", input: "export { type x } from \"m\";", output: ""},
|
||||
{title: "ExportDeclaration#7", input: "export { type x } from \"m\";", output: "export {} from \"m\";", vms: true},
|
||||
}
|
||||
|
||||
for _, rec := range data {
|
||||
t.Run(rec.title, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
file := parsetestutil.ParseTypeScript(rec.input, rec.jsx)
|
||||
parsetestutil.CheckDiagnostics(t, file)
|
||||
compilerOptions := &core.CompilerOptions{}
|
||||
if rec.vms {
|
||||
compilerOptions.VerbatimModuleSyntax = core.TSTrue
|
||||
}
|
||||
emittestutil.CheckEmit(t, nil, tstransforms.NewTypeEraserTransformer(&transformers.TransformOptions{CompilerOptions: compilerOptions, Context: printer.NewEmitContext()}).TransformSourceFile(file), rec.output)
|
||||
})
|
||||
}
|
||||
}
|
||||
511
tools/tsgo/internal/transformers/tstransforms/typeserializer.go
Normal file
511
tools/tsgo/internal/transformers/tstransforms/typeserializer.go
Normal file
@@ -0,0 +1,511 @@
|
||||
package tstransforms
|
||||
|
||||
import (
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/core"
|
||||
"github.com/microsoft/typescript-go/internal/debug"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
"github.com/microsoft/typescript-go/internal/transformers"
|
||||
)
|
||||
|
||||
type metadataSerializer struct {
|
||||
resolver printer.EmitResolver
|
||||
languageVersion core.ScriptTarget
|
||||
strictNullChecks bool
|
||||
f *printer.NodeFactory
|
||||
ec *printer.EmitContext
|
||||
c metadataSerializerContext
|
||||
}
|
||||
|
||||
type metadataSerializerContext struct {
|
||||
currentLexicalScope *ast.Node
|
||||
currentNameScope *ast.Node
|
||||
serializingConditionalTypeBranch bool
|
||||
}
|
||||
|
||||
func newMetadataSerializer(resolver printer.EmitResolver, f *printer.NodeFactory, ec *printer.EmitContext, languageVersion core.ScriptTarget, strictNullChecks bool) *metadataSerializer {
|
||||
return &metadataSerializer{resolver: resolver, languageVersion: languageVersion, f: f, ec: ec, strictNullChecks: strictNullChecks}
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) setContext(ctx metadataSerializerContext) {
|
||||
s.c = ctx
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) SerializeTypeOfNode(ctx metadataSerializerContext, node *ast.Node, container *ast.Node) *ast.Node {
|
||||
oldCtx := s.c
|
||||
s.c = ctx
|
||||
defer s.setContext(oldCtx)
|
||||
return s.serializeTypeOfNode(node, container)
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) SerializeParameterTypesOfNode(ctx metadataSerializerContext, node *ast.Node, container *ast.Node) *ast.Node {
|
||||
oldCtx := s.c
|
||||
s.c = ctx
|
||||
defer s.setContext(oldCtx)
|
||||
return s.serializeParameterTypesOfNode(node, container)
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) SerializeReturnTypeOfNode(ctx metadataSerializerContext, node *ast.Node) *ast.Node {
|
||||
oldCtx := s.c
|
||||
s.c = ctx
|
||||
defer s.setContext(oldCtx)
|
||||
return s.serializeReturnTypeOfNode(node)
|
||||
}
|
||||
|
||||
func GetSetAccessorValueParameter(node *ast.SetAccessorDeclaration) *ast.Node {
|
||||
if node != nil && len(node.Parameters.Nodes) > 0 {
|
||||
if len(node.Parameters.Nodes) >= 2 && ast.IsThisParameter(node.Parameters.Nodes[0]) {
|
||||
return node.Parameters.Nodes[1]
|
||||
}
|
||||
return node.Parameters.Nodes[0]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the type annotation for the value parameter.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
func getSetAccessorTypeAnnotationNode(node *ast.SetAccessorDeclaration) *ast.Node {
|
||||
p := GetSetAccessorValueParameter(node)
|
||||
if p != nil && p.Type() != nil {
|
||||
return p.Type()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getAccessorTypeNode(node *ast.Node, container *ast.Node) *ast.Node {
|
||||
accessors := ast.GetAllAccessorDeclarations(container.Members(), node)
|
||||
if accessors.SetAccessor != nil {
|
||||
return getSetAccessorTypeAnnotationNode(accessors.SetAccessor)
|
||||
}
|
||||
if accessors.GetAccessor != nil {
|
||||
return accessors.GetAccessor.Type
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the type of a node for use with decorator type metadata.
|
||||
* @param node The node that should have its type serialized.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeTypeOfNode(node *ast.Node, container *ast.Node) *ast.Node {
|
||||
switch node.Kind {
|
||||
case ast.KindPropertyDeclaration, ast.KindParameter:
|
||||
return s.serializeTypeNode(node.Type())
|
||||
case ast.KindGetAccessor, ast.KindSetAccessor:
|
||||
return s.serializeTypeNode(getAccessorTypeNode(node, container))
|
||||
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindMethodDeclaration:
|
||||
return s.f.NewIdentifier("Function")
|
||||
default:
|
||||
return s.f.NewVoidZeroExpression()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the type of a node for use with decorator type metadata.
|
||||
* @param node The node that should have its type serialized.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeParameterTypesOfNode(node *ast.Node, container *ast.Node) *ast.Node {
|
||||
var valueDeclaration *ast.Node
|
||||
if ast.IsClassLike(node) {
|
||||
valueDeclaration = ast.GetFirstConstructorWithBody(node)
|
||||
} else if ast.IsFunctionLike(node) && ast.NodeIsPresent(node.Body()) {
|
||||
valueDeclaration = node
|
||||
}
|
||||
|
||||
if valueDeclaration == nil {
|
||||
return s.f.NewArrayLiteralExpression(s.f.NewNodeList([]*ast.Node{}), false)
|
||||
}
|
||||
|
||||
var expressions []*ast.Node
|
||||
parameters := getParametersOfDecoratedDeclaration(valueDeclaration, container)
|
||||
for i, parameter := range parameters.Nodes {
|
||||
if i == 0 && ast.IsIdentifier(parameter.Name()) && parameter.Name().Text() == "this" {
|
||||
continue
|
||||
}
|
||||
if parameter.AsParameterDeclaration().DotDotDotToken != nil {
|
||||
expressions = append(expressions, s.serializeTypeNode(ast.GetRestParameterElementType(parameter.Type())))
|
||||
} else {
|
||||
expressions = append(expressions, s.serializeTypeOfNode(parameter, container))
|
||||
}
|
||||
}
|
||||
return s.f.NewArrayLiteralExpression(s.f.NewNodeList(expressions), false)
|
||||
}
|
||||
|
||||
func getParametersOfDecoratedDeclaration(node *ast.Node, container *ast.Node) *ast.NodeList {
|
||||
if container != nil && node.Kind == ast.KindGetAccessor {
|
||||
acc := ast.GetAllAccessorDeclarations(container.Members(), node)
|
||||
if acc.SetAccessor != nil {
|
||||
return acc.SetAccessor.Parameters
|
||||
}
|
||||
}
|
||||
return node.ParameterList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes the return type of a node for use with decorator type metadata.
|
||||
* @param node The node that should have its return type serialized.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeReturnTypeOfNode(node *ast.Node) *ast.Node {
|
||||
if ast.IsFunctionLike(node) && node.Type() != nil {
|
||||
return s.serializeTypeNode(node.Type())
|
||||
} else if ast.IsAsyncFunction(node) {
|
||||
return s.f.NewIdentifier("Promise")
|
||||
}
|
||||
return s.f.NewVoidZeroExpression()
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a type node for use with decorator type metadata.
|
||||
*
|
||||
* Types are serialized in the following fashion:
|
||||
* - Void types point to "undefined" (e.g. "void 0")
|
||||
* - Function and Constructor types point to the global "Function" constructor.
|
||||
* - Interface types with a call or construct signature types point to the global
|
||||
* "Function" constructor.
|
||||
* - Array and Tuple types point to the global "Array" constructor.
|
||||
* - Type predicates and booleans point to the global "Boolean" constructor.
|
||||
* - String literal types and strings point to the global "String" constructor.
|
||||
* - Enum and number types point to the global "Number" constructor.
|
||||
* - Symbol types point to the global "Symbol" constructor.
|
||||
* - Type references to classes (or class-like variables) point to the constructor for the class.
|
||||
* - Anything else points to the global "Object" constructor.
|
||||
*
|
||||
* @param node The type node to serialize.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeTypeNode(node *ast.Node) *ast.Node {
|
||||
if node == nil {
|
||||
return s.f.NewIdentifier("Object")
|
||||
}
|
||||
|
||||
node = ast.SkipTypeParentheses(node)
|
||||
|
||||
switch node.Kind {
|
||||
case ast.KindVoidKeyword, ast.KindUndefinedKeyword, ast.KindNeverKeyword:
|
||||
return s.f.NewVoidZeroExpression()
|
||||
case ast.KindFunctionType, ast.KindConstructorType:
|
||||
return s.f.NewIdentifier("Function")
|
||||
case ast.KindArrayType, ast.KindTupleType:
|
||||
return s.f.NewIdentifier("Array")
|
||||
case ast.KindTypePredicate:
|
||||
if node.AsTypePredicateNode().AssertsModifier != nil {
|
||||
return s.f.NewVoidZeroExpression()
|
||||
}
|
||||
return s.f.NewIdentifier("Boolean")
|
||||
case ast.KindBooleanKeyword:
|
||||
return s.f.NewIdentifier("Boolean")
|
||||
case ast.KindTemplateLiteralType, ast.KindStringKeyword:
|
||||
return s.f.NewIdentifier("String")
|
||||
case ast.KindObjectKeyword:
|
||||
return s.f.NewIdentifier("Object")
|
||||
case ast.KindLiteralType:
|
||||
return s.serializeLiteralOfLiteralTypeNode(node.AsLiteralTypeNode().Literal)
|
||||
case ast.KindNumberKeyword:
|
||||
return s.f.NewIdentifier("Number")
|
||||
case ast.KindBigIntKeyword:
|
||||
return s.serializeBigIntConstructor()
|
||||
case ast.KindSymbolKeyword:
|
||||
return s.f.NewIdentifier("Symbol")
|
||||
case ast.KindTypeReference:
|
||||
return s.serializeTypeReferenceNode(node.AsTypeReferenceNode())
|
||||
case ast.KindIntersectionType:
|
||||
return s.serializeUnionOrIntersectionConstituents(node.AsIntersectionTypeNode().Types.Nodes, true)
|
||||
case ast.KindUnionType:
|
||||
return s.serializeUnionOrIntersectionConstituents(node.AsUnionTypeNode().Types.Nodes, false)
|
||||
case ast.KindConditionalType:
|
||||
oldState := s.c.serializingConditionalTypeBranch
|
||||
s.c.serializingConditionalTypeBranch = true
|
||||
defer func() { s.c.serializingConditionalTypeBranch = oldState }()
|
||||
return s.serializeUnionOrIntersectionConstituents([]*ast.Node{node.AsConditionalTypeNode().TrueType, node.AsConditionalTypeNode().FalseType}, false)
|
||||
case ast.KindTypeOperator:
|
||||
if node.AsTypeOperatorNode().Operator == ast.KindReadonlyKeyword {
|
||||
return s.serializeTypeNode(node.Type())
|
||||
}
|
||||
// TODO: why is `unique symbol` not handled as `Symbol`? This falls back to `Object`
|
||||
case ast.KindTypeQuery, ast.KindIndexedAccessType, ast.KindMappedType, ast.KindTypeLiteral, ast.KindAnyKeyword, ast.KindUnknownKeyword, ast.KindThisType, ast.KindImportType:
|
||||
break
|
||||
|
||||
// handle JSDoc types from an invalid parse
|
||||
case ast.KindJSDocAllType, ast.KindJSDocVariadicType:
|
||||
break
|
||||
case ast.KindJSDocNullableType, ast.KindJSDocNonNullableType, ast.KindJSDocOptionalType:
|
||||
return s.serializeTypeNode(node.Type())
|
||||
default:
|
||||
debug.FailBadSyntaxKind(node)
|
||||
return nil
|
||||
}
|
||||
return s.f.NewIdentifier("Object")
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) serializeUnionOrIntersectionConstituents(types []*ast.Node, isIntersection bool) *ast.Node {
|
||||
// Note when updating logic here also update `getEntityNameForDecoratorMetadata` in checker.ts so that aliases can be marked as referenced
|
||||
var serializedType *ast.Node
|
||||
for _, typeNode := range types {
|
||||
typeNode = ast.SkipTypeParentheses(typeNode)
|
||||
if typeNode.Kind == ast.KindNeverKeyword {
|
||||
if isIntersection {
|
||||
return s.f.NewVoidZeroExpression() // Reduce to `never` in an intersection
|
||||
}
|
||||
continue // Elide `never` in a union
|
||||
}
|
||||
|
||||
if typeNode.Kind == ast.KindUnknownKeyword {
|
||||
if !isIntersection {
|
||||
return s.f.NewIdentifier("Object") // Reduce to `unknown` in a union
|
||||
}
|
||||
continue // Elide `unknown` in an intersection
|
||||
}
|
||||
|
||||
if typeNode.Kind == ast.KindAnyKeyword {
|
||||
return s.f.NewIdentifier("Object") // Reduce to `any` in a union or intersection
|
||||
}
|
||||
|
||||
if !s.strictNullChecks && ((ast.IsLiteralTypeNode(typeNode) && typeNode.AsLiteralTypeNode().Literal.Kind == ast.KindNullKeyword) || typeNode.Kind == ast.KindUndefinedKeyword) {
|
||||
continue // Elide null and undefined from unions for metadata, just like what we did prior to the implementation of strict null checks
|
||||
}
|
||||
|
||||
serializedConstituent := s.serializeTypeNode(typeNode)
|
||||
if ast.IsIdentifier(serializedConstituent) && serializedConstituent.AsIdentifier().Text == "Object" {
|
||||
// One of the individual is global object, return immediately
|
||||
return serializedConstituent
|
||||
}
|
||||
|
||||
// If there exists union that is not `void 0` expression, check if the the common type is identifier.
|
||||
// anything more complex and we will just default to Object
|
||||
if serializedType != nil {
|
||||
// Different types
|
||||
if !s.equateSerializedTypeNodes(serializedType, serializedConstituent) {
|
||||
return s.f.NewIdentifier("Object")
|
||||
}
|
||||
} else {
|
||||
// Initialize the union type
|
||||
serializedType = serializedConstituent
|
||||
}
|
||||
}
|
||||
|
||||
// If we were able to find common type, use it
|
||||
if serializedType != nil {
|
||||
return serializedType
|
||||
}
|
||||
return s.f.NewVoidZeroExpression() // Fallback is only hit if all union constituents are null/undefined/never
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) serializeLiteralOfLiteralTypeNode(node *ast.Node) *ast.Node {
|
||||
switch node.Kind {
|
||||
case ast.KindStringLiteral, ast.KindNoSubstitutionTemplateLiteral:
|
||||
return s.f.NewIdentifier("String")
|
||||
case ast.KindPrefixUnaryExpression:
|
||||
operand := node.AsPrefixUnaryExpression().Operand
|
||||
switch operand.Kind {
|
||||
case ast.KindNumericLiteral, ast.KindBigIntLiteral:
|
||||
return s.serializeLiteralOfLiteralTypeNode(operand)
|
||||
default:
|
||||
debug.FailBadSyntaxKind(operand)
|
||||
}
|
||||
case ast.KindNumericLiteral:
|
||||
return s.f.NewIdentifier("Number")
|
||||
case ast.KindBigIntLiteral:
|
||||
return s.serializeBigIntConstructor()
|
||||
case ast.KindTrueKeyword, ast.KindFalseKeyword:
|
||||
return s.f.NewIdentifier("Boolean")
|
||||
case ast.KindNullKeyword:
|
||||
return s.f.NewVoidZeroExpression()
|
||||
default:
|
||||
debug.FailBadSyntaxKind(node)
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes a TypeReferenceNode to an appropriate JS constructor value for use with decorator type metadata.
|
||||
* @param node The type reference node.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeTypeReferenceNode(node *ast.TypeReferenceNode) *ast.Node {
|
||||
serialScope := s.c.currentNameScope
|
||||
if serialScope == nil {
|
||||
serialScope = s.c.currentLexicalScope
|
||||
}
|
||||
kind := s.resolver.GetTypeReferenceSerializationKind(s.ec.ParseNode(node.TypeName), s.ec.ParseNode(serialScope))
|
||||
switch kind {
|
||||
case printer.TypeReferenceSerializationKindUnknown:
|
||||
// From conditional type type reference that cannot be resolved is Similar to any or unknown
|
||||
if s.c.serializingConditionalTypeBranch {
|
||||
return s.f.NewIdentifier("Object")
|
||||
}
|
||||
|
||||
serialized := s.serializeEntityNameAsExpressionFallback(node.TypeName)
|
||||
temp := s.f.NewTempVariable()
|
||||
s.ec.AddVariableDeclaration(temp)
|
||||
return s.f.NewConditionalExpression(
|
||||
s.f.NewTypeCheck(s.f.NewAssignmentExpression(temp, serialized), "function"),
|
||||
s.f.NewToken(ast.KindQuestionToken),
|
||||
temp,
|
||||
s.f.NewToken(ast.KindColonToken),
|
||||
s.f.NewIdentifier("Object"),
|
||||
)
|
||||
|
||||
case printer.TypeReferenceSerializationKindTypeWithConstructSignatureAndValue:
|
||||
return s.serializeEntityNameAsExpression(node.TypeName)
|
||||
|
||||
case printer.TypeReferenceSerializationKindVoidNullableOrNeverType:
|
||||
return s.f.NewVoidZeroExpression()
|
||||
|
||||
case printer.TypeReferenceSerializationKindBigIntLikeType:
|
||||
return s.serializeBigIntConstructor()
|
||||
|
||||
case printer.TypeReferenceSerializationKindBooleanType:
|
||||
return s.f.NewIdentifier("Boolean")
|
||||
|
||||
case printer.TypeReferenceSerializationKindNumberLikeType:
|
||||
return s.f.NewIdentifier("Number")
|
||||
|
||||
case printer.TypeReferenceSerializationKindStringLikeType:
|
||||
return s.f.NewIdentifier("String")
|
||||
|
||||
case printer.TypeReferenceSerializationKindArrayLikeType:
|
||||
return s.f.NewIdentifier("Array")
|
||||
|
||||
case printer.TypeReferenceSerializationKindESSymbolType:
|
||||
return s.f.NewIdentifier("Symbol")
|
||||
|
||||
case printer.TypeReferenceSerializationKindTypeWithCallSignature:
|
||||
return s.f.NewIdentifier("Function")
|
||||
|
||||
case printer.TypeReferenceSerializationKindPromise:
|
||||
return s.f.NewIdentifier("Promise")
|
||||
|
||||
case printer.TypeReferenceSerializationKindObjectType:
|
||||
return s.f.NewIdentifier("Object")
|
||||
default:
|
||||
debug.AssertNever(kind, "unknown type reference serialization kind")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) serializeBigIntConstructor() *ast.Node {
|
||||
if s.languageVersion >= core.ScriptTargetES2020 {
|
||||
return s.f.NewIdentifier("BigInt")
|
||||
}
|
||||
return s.f.NewConditionalExpression(
|
||||
s.f.NewTypeCheck(s.f.NewIdentifier("BigInt"), "function"),
|
||||
s.f.NewToken(ast.KindQuestionToken),
|
||||
s.f.NewIdentifier("BigInt"),
|
||||
s.f.NewToken(ast.KindColonToken),
|
||||
s.f.NewIdentifier("Object"),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an entity name as an expression for decorator type metadata.
|
||||
* @param node The entity name to serialize.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeEntityNameAsExpression(node *ast.EntityName) *ast.Node {
|
||||
switch node.Kind {
|
||||
case ast.KindIdentifier:
|
||||
// Create a clone of the name with a new parent, and treat it as if it were
|
||||
// a source tree node for the purposes of the checker.
|
||||
name := node.Clone(s.f)
|
||||
name.Loc = node.Loc
|
||||
s.ec.UnsetOriginal(name) // make this identifier emulate a parse node, making it behave correctly when inspected by the module transforms
|
||||
name.Parent = s.ec.ParseNode(s.c.currentLexicalScope) //nolint:customlint // ensure the parent is set to a parse tree node.
|
||||
return name
|
||||
case ast.KindQualifiedName:
|
||||
return s.serializeQualifiedNameAsExpression(node.AsQualifiedName())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an qualified name as an expression for decorator type metadata.
|
||||
* @param node The qualified name to serialize.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeQualifiedNameAsExpression(node *ast.QualifiedName) *ast.Node {
|
||||
return s.f.NewPropertyAccessExpression(s.serializeEntityNameAsExpression(node.Left), nil, node.Right, ast.NodeFlagsNone)
|
||||
}
|
||||
|
||||
/**
|
||||
* Serializes an entity name which may not exist at runtime, but whose access shouldn't throw
|
||||
* @param node The entity name to serialize.
|
||||
*/
|
||||
func (s *metadataSerializer) serializeEntityNameAsExpressionFallback(node *ast.EntityName) *ast.Node {
|
||||
if node.Kind == ast.KindIdentifier {
|
||||
// A -> typeof A !== "undefined" && A
|
||||
copied := s.serializeEntityNameAsExpression(node)
|
||||
return s.createCheckedValue(copied, copied)
|
||||
}
|
||||
if node.AsQualifiedName().Left.Kind == ast.KindIdentifier {
|
||||
// A.B -> typeof A !== "undefined" && A.B
|
||||
return s.createCheckedValue(s.serializeEntityNameAsExpression(node.AsQualifiedName().Left), s.serializeEntityNameAsExpression(node))
|
||||
}
|
||||
// A.B.C -> typeof A !== "undefined" && (_a = A.B) !== void 0 && _a.C
|
||||
left := s.serializeEntityNameAsExpressionFallback(node.AsQualifiedName().Left)
|
||||
temp := s.f.NewTempVariable()
|
||||
s.ec.AddVariableDeclaration(temp)
|
||||
return s.f.NewLogicalANDExpression(
|
||||
s.f.NewLogicalANDExpression(
|
||||
left.AsBinaryExpression().Left,
|
||||
s.f.NewStrictInequalityExpression(s.f.NewAssignmentExpression(temp, left.AsBinaryExpression().Right), s.f.NewVoidZeroExpression()),
|
||||
),
|
||||
s.f.NewPropertyAccessExpression(temp, nil, node.AsQualifiedName().Right, ast.NodeFlagsNone),
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces an expression that results in `right` if `left` is not undefined at runtime:
|
||||
*
|
||||
* ```
|
||||
* typeof left !== "undefined" && right
|
||||
* ```
|
||||
*
|
||||
* We use `typeof L !== "undefined"` (rather than `L !== undefined`) since `L` may not be declared.
|
||||
* It's acceptable for this expression to result in `false` at runtime, as the result is intended to be
|
||||
* further checked by any containing expression.
|
||||
*/
|
||||
func (s *metadataSerializer) createCheckedValue(left *ast.Node, right *ast.Node) *ast.Node {
|
||||
return s.f.NewLogicalANDExpression(
|
||||
s.f.NewStrictInequalityExpression(s.f.NewTypeOfExpression(left), s.f.NewStringLiteral("undefined", ast.TokenFlagsNone)),
|
||||
right,
|
||||
)
|
||||
}
|
||||
|
||||
func (s *metadataSerializer) equateSerializedTypeNodes(left *ast.Node, right *ast.Node) bool {
|
||||
// temp vars used in fallback
|
||||
if transformers.IsGeneratedIdentifier(s.ec, left) {
|
||||
return transformers.IsGeneratedIdentifier(s.ec, right)
|
||||
}
|
||||
// entity names
|
||||
if ast.IsIdentifier(left) {
|
||||
return ast.IsIdentifier(right) && left.Text() == right.Text()
|
||||
}
|
||||
if ast.IsPropertyAccessExpression(left) {
|
||||
return ast.IsPropertyAccessExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression()) && s.equateSerializedTypeNodes(left.Name(), right.Name())
|
||||
}
|
||||
// `void 0`
|
||||
if ast.IsVoidExpression(left) {
|
||||
return ast.IsVoidExpression(right) && ast.IsNumericLiteral(left.Expression()) && ast.IsNumericLiteral(right.Expression()) && left.Expression().Text() == "0" && right.Expression().Text() == "0"
|
||||
}
|
||||
// `"undefined"` or `"function"` in `typeof` checks
|
||||
if ast.IsStringLiteral(left) {
|
||||
return ast.IsStringLiteral(right) && left.Text() == right.Text()
|
||||
}
|
||||
// used in `typeof` checks for fallback
|
||||
if ast.IsTypeOfExpression(left) {
|
||||
return ast.IsTypeOfExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression())
|
||||
}
|
||||
// parens in `typeof` checks with temps
|
||||
if ast.IsParenthesizedExpression(left) {
|
||||
return ast.IsParenthesizedExpression(right) && s.equateSerializedTypeNodes(left.Expression(), right.Expression())
|
||||
}
|
||||
// conditionals used in fallback
|
||||
if ast.IsConditionalExpression(left) {
|
||||
return ast.IsConditionalExpression(right) && s.equateSerializedTypeNodes(left.AsConditionalExpression().Condition, right.AsConditionalExpression().Condition) && s.equateSerializedTypeNodes(left.AsConditionalExpression().WhenTrue, right.AsConditionalExpression().WhenTrue) && s.equateSerializedTypeNodes(left.AsConditionalExpression().WhenFalse, right.AsConditionalExpression().WhenFalse)
|
||||
}
|
||||
// logical binary and assignments used in fallback
|
||||
if ast.IsBinaryExpression(left) {
|
||||
return ast.IsBinaryExpression(right) && left.AsBinaryExpression().OperatorToken.Kind == right.AsBinaryExpression().OperatorToken.Kind && s.equateSerializedTypeNodes(left.AsBinaryExpression().Left, right.AsBinaryExpression().Left) && s.equateSerializedTypeNodes(left.AsBinaryExpression().Right, right.AsBinaryExpression().Right)
|
||||
}
|
||||
return false
|
||||
}
|
||||
29
tools/tsgo/internal/transformers/tstransforms/utilities.go
Normal file
29
tools/tsgo/internal/transformers/tstransforms/utilities.go
Normal file
@@ -0,0 +1,29 @@
|
||||
package tstransforms
|
||||
|
||||
import (
|
||||
"github.com/microsoft/typescript-go/internal/ast"
|
||||
"github.com/microsoft/typescript-go/internal/jsnum"
|
||||
"github.com/microsoft/typescript-go/internal/printer"
|
||||
)
|
||||
|
||||
func constantExpression(value any, factory *printer.NodeFactory) *ast.Expression {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
return factory.NewStringLiteral(value, ast.TokenFlagsNone)
|
||||
case jsnum.Number:
|
||||
if value.IsInf() {
|
||||
if value > 0 {
|
||||
return factory.NewIdentifier("Infinity")
|
||||
}
|
||||
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, factory.NewIdentifier("Infinity"))
|
||||
}
|
||||
if value.IsNaN() {
|
||||
return factory.NewIdentifier("NaN")
|
||||
}
|
||||
if value < 0 {
|
||||
return factory.NewPrefixUnaryExpression(ast.KindMinusToken, constantExpression(-value, factory))
|
||||
}
|
||||
return factory.NewNumericLiteral(value.String(), ast.TokenFlagsNone)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user