Skip to content

Commit 1e34fb9

Browse files
authored
Add stubs for missing downlevel feature transforms, organize transforms by purpose (#1270)
1 parent 64e8a99 commit 1e34fb9

38 files changed

+1607
-1260
lines changed

internal/compiler/emitter.go

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"encoding/base64"
55

66
"github.com/microsoft/typescript-go/internal/ast"
7+
"github.com/microsoft/typescript-go/internal/binder"
78
"github.com/microsoft/typescript-go/internal/core"
89
"github.com/microsoft/typescript-go/internal/diagnostics"
910
"github.com/microsoft/typescript-go/internal/outputpaths"
@@ -12,6 +13,10 @@ import (
1213
"github.com/microsoft/typescript-go/internal/stringutil"
1314
"github.com/microsoft/typescript-go/internal/transformers"
1415
"github.com/microsoft/typescript-go/internal/transformers/declarations"
16+
"github.com/microsoft/typescript-go/internal/transformers/estransforms"
17+
"github.com/microsoft/typescript-go/internal/transformers/jsxtransforms"
18+
"github.com/microsoft/typescript-go/internal/transformers/moduletransforms"
19+
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
1520
"github.com/microsoft/typescript-go/internal/tspath"
1621
)
1722

@@ -49,6 +54,73 @@ func (e *emitter) getDeclarationTransformers(emitContext *printer.EmitContext, s
4954
return []*declarations.DeclarationTransformer{transform}
5055
}
5156

57+
func getModuleTransformer(emitContext *printer.EmitContext, options *core.CompilerOptions, resolver binder.ReferenceResolver, getEmitModuleFormatOfFile func(file ast.HasFileName) core.ModuleKind) *transformers.Transformer {
58+
switch options.GetEmitModuleKind() {
59+
case core.ModuleKindPreserve:
60+
// `ESModuleTransformer` contains logic for preserving CJS input syntax in `--module preserve`
61+
return moduletransforms.NewESModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
62+
63+
case core.ModuleKindESNext,
64+
core.ModuleKindES2022,
65+
core.ModuleKindES2020,
66+
core.ModuleKindES2015,
67+
core.ModuleKindNode18,
68+
core.ModuleKindNode16,
69+
core.ModuleKindNodeNext,
70+
core.ModuleKindCommonJS:
71+
return moduletransforms.NewImpliedModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
72+
73+
default:
74+
return moduletransforms.NewCommonJSModuleTransformer(emitContext, options, resolver, getEmitModuleFormatOfFile)
75+
}
76+
}
77+
78+
func getScriptTransformers(emitContext *printer.EmitContext, host printer.EmitHost, sourceFile *ast.SourceFile) []*transformers.Transformer {
79+
var tx []*transformers.Transformer
80+
options := host.Options()
81+
82+
// JS files don't use reference calculations as they don't do import elision, no need to calculate it
83+
importElisionEnabled := !options.VerbatimModuleSyntax.IsTrue() && !ast.IsInJSFile(sourceFile.AsNode())
84+
85+
var emitResolver printer.EmitResolver
86+
var referenceResolver binder.ReferenceResolver
87+
if importElisionEnabled || options.GetJSXTransformEnabled() {
88+
emitResolver = host.GetEmitResolver(sourceFile, false /*skipDiagnostics*/) // !!! conditionally skip diagnostics
89+
emitResolver.MarkLinkedReferencesRecursively(sourceFile)
90+
referenceResolver = emitResolver
91+
} else {
92+
referenceResolver = binder.NewReferenceResolver(options, binder.ReferenceResolverHooks{})
93+
}
94+
95+
// transform TypeScript syntax
96+
{
97+
// erase types
98+
tx = append(tx, tstransforms.NewTypeEraserTransformer(emitContext, options))
99+
100+
// elide imports
101+
if importElisionEnabled {
102+
tx = append(tx, tstransforms.NewImportElisionTransformer(emitContext, options, emitResolver))
103+
}
104+
105+
// transform `enum`, `namespace`, and parameter properties
106+
tx = append(tx, tstransforms.NewRuntimeSyntaxTransformer(emitContext, options, referenceResolver))
107+
}
108+
109+
// !!! transform legacy decorator syntax
110+
if options.GetJSXTransformEnabled() {
111+
tx = append(tx, jsxtransforms.NewJSXTransformer(emitContext, options, emitResolver))
112+
}
113+
114+
downleveler := estransforms.GetESTransformer(options, emitContext)
115+
if downleveler != nil {
116+
tx = append(tx, downleveler)
117+
}
118+
119+
// transform module syntax
120+
tx = append(tx, getModuleTransformer(emitContext, options, referenceResolver, host.GetEmitModuleFormatOfFile))
121+
return tx
122+
}
123+
52124
func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sourceMapFilePath string) {
53125
options := e.host.Options()
54126

@@ -61,7 +133,7 @@ func (e *emitter) emitJSFile(sourceFile *ast.SourceFile, jsFilePath string, sour
61133
}
62134

63135
emitContext := printer.NewEmitContext()
64-
for _, transformer := range transformers.GetScriptTransformers(emitContext, e.host, sourceFile) {
136+
for _, transformer := range getScriptTransformers(emitContext, e.host, sourceFile) {
65137
sourceFile = transformer.TransformSourceFile(sourceFile)
66138
}
67139

internal/printer/printer_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
"github.com/microsoft/typescript-go/internal/printer"
99
"github.com/microsoft/typescript-go/internal/testutil/emittestutil"
1010
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
11-
"github.com/microsoft/typescript-go/internal/transformers"
11+
"github.com/microsoft/typescript-go/internal/transformers/tstransforms"
1212
)
1313

1414
func TestEmit(t *testing.T) {
@@ -2503,7 +2503,7 @@ func TestPartiallyEmittedExpression(t *testing.T) {
25032503
.expression;`, false /*jsx*/)
25042504

25052505
emitContext := printer.NewEmitContext()
2506-
file = transformers.NewTypeEraserTransformer(emitContext, compilerOptions).TransformSourceFile(file)
2506+
file = tstransforms.NewTypeEraserTransformer(emitContext, compilerOptions).TransformSourceFile(file)
25072507
emittestutil.CheckEmit(t, emitContext, file.AsSourceFile(), `return container.parent
25082508
.left
25092509
.expression

internal/transformers/chain.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package transformers
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
)
7+
8+
type chainedTransformer struct {
9+
Transformer
10+
components []*Transformer
11+
}
12+
13+
func (ch *chainedTransformer) visit(node *ast.Node) *ast.Node {
14+
if node.Kind != ast.KindSourceFile {
15+
panic("Chained transform passed non-sourcefile initial node")
16+
}
17+
result := node.AsSourceFile()
18+
for _, t := range ch.components {
19+
result = t.TransformSourceFile(result)
20+
}
21+
return result.AsNode()
22+
}
23+
24+
type TransformerFactory = func(emitContext *printer.EmitContext) *Transformer
25+
26+
// Chains transforms in left-to-right order, running them one at a time in order (as opposed to interleaved at each node)
27+
// - the resulting combined transform only operates on SourceFile nodes
28+
func Chain(transforms ...TransformerFactory) TransformerFactory {
29+
if len(transforms) < 2 {
30+
if len(transforms) == 0 {
31+
panic("Expected some number of transforms to chain, but got none")
32+
}
33+
return transforms[0]
34+
}
35+
return func(emitContext *printer.EmitContext) *Transformer {
36+
constructed := make([]*Transformer, 0, len(transforms))
37+
for _, t := range transforms {
38+
// TODO: flatten nested chains?
39+
constructed = append(constructed, t(emitContext))
40+
}
41+
ch := &chainedTransformer{components: constructed}
42+
return ch.NewTransformer(ch.visit, emitContext)
43+
}
44+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
type asyncTransformer struct {
10+
transformers.Transformer
11+
}
12+
13+
func (ch *asyncTransformer) visit(node *ast.Node) *ast.Node {
14+
return node // !!!
15+
}
16+
17+
func newAsyncTransformer(emitContext *printer.EmitContext) *transformers.Transformer {
18+
tx := &asyncTransformer{}
19+
return tx.NewTransformer(tx.visit, emitContext)
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
type classFieldsTransformer struct {
10+
transformers.Transformer
11+
}
12+
13+
func (ch *classFieldsTransformer) visit(node *ast.Node) *ast.Node {
14+
return node // !!!
15+
}
16+
17+
func newClassFieldsTransformer(emitContext *printer.EmitContext) *transformers.Transformer {
18+
tx := &classFieldsTransformer{}
19+
return tx.NewTransformer(tx.visit, emitContext)
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
type classStaticBlockTransformer struct {
10+
transformers.Transformer
11+
}
12+
13+
func (ch *classStaticBlockTransformer) visit(node *ast.Node) *ast.Node {
14+
return node // !!!
15+
}
16+
17+
func newClassStaticBlockTransformer(emitContext *printer.EmitContext) *transformers.Transformer {
18+
tx := &classStaticBlockTransformer{}
19+
return tx.NewTransformer(tx.visit, emitContext)
20+
}

internal/transformers/classthis.go renamed to internal/transformers/estransforms/classthis.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package transformers
1+
package estransforms
22

33
import (
44
"github.com/microsoft/typescript-go/internal/ast"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/core"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
// !!! TODO: This fixed layering scheme assumes you can't swap out the es decorator transform for the legacy one,
10+
// or the proper es class field transform for the legacy one
11+
var (
12+
NewESNextTransformer = transformers.Chain(newESDecoratorTransformer, newUsingDeclarationTransformer)
13+
// 2025: only module system syntax (import attributes, json modules), untransformed regex modifiers
14+
// 2024: no new downlevel syntax
15+
// 2023: no new downlevel syntax
16+
NewES2022Transformer = transformers.Chain(NewESNextTransformer, newClassStaticBlockTransformer, newClassFieldsTransformer) // !!! top level await? not transformed, just errored on at lower targets - also more of a module system feature anyway
17+
NewES2021Transformer = transformers.Chain(NewES2022Transformer, newLogicalAssignmentTransformer) // !!! numeric seperators? always elided by printer?
18+
NewES2020Transformer = transformers.Chain(NewES2021Transformer, newNullishCoalescingTransformer, newOptionalChainTransformer) // also dynamic import - module system feature
19+
NewES2019Transformer = transformers.Chain(NewES2020Transformer, newOptionalCatchTransformer)
20+
NewES2018Transformer = transformers.Chain(NewES2019Transformer, newObjectRestSpreadTransformer, newforawaitTransformer)
21+
NewES2017Transformer = transformers.Chain(NewES2018Transformer, newAsyncTransformer)
22+
NewES2016Transformer = transformers.Chain(NewES2017Transformer, newExponentiationTransformer)
23+
)
24+
25+
func GetESTransformer(options *core.CompilerOptions, emitContext *printer.EmitContext) *transformers.Transformer {
26+
switch options.GetEmitScriptTarget() {
27+
case core.ScriptTargetESNext:
28+
return nil // no transforms needed
29+
case /*core.ScriptTargetES2025,*/ core.ScriptTargetES2024, core.ScriptTargetES2023, core.ScriptTargetES2022:
30+
return NewESNextTransformer(emitContext)
31+
case core.ScriptTargetES2021:
32+
return NewES2022Transformer(emitContext)
33+
case core.ScriptTargetES2020:
34+
return NewES2021Transformer(emitContext)
35+
case core.ScriptTargetES2019:
36+
return NewES2020Transformer(emitContext)
37+
case core.ScriptTargetES2018:
38+
return NewES2019Transformer(emitContext)
39+
case core.ScriptTargetES2017:
40+
return NewES2018Transformer(emitContext)
41+
case core.ScriptTargetES2016:
42+
return NewES2017Transformer(emitContext)
43+
default: // other, older, option, transform maximally
44+
return NewES2016Transformer(emitContext)
45+
}
46+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
type esDecoratorTransformer struct {
10+
transformers.Transformer
11+
}
12+
13+
func (ch *esDecoratorTransformer) visit(node *ast.Node) *ast.Node {
14+
return node // !!!
15+
}
16+
17+
func newESDecoratorTransformer(emitContext *printer.EmitContext) *transformers.Transformer {
18+
tx := &esDecoratorTransformer{}
19+
return tx.NewTransformer(tx.visit, emitContext)
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package estransforms
2+
3+
import (
4+
"github.com/microsoft/typescript-go/internal/ast"
5+
"github.com/microsoft/typescript-go/internal/printer"
6+
"github.com/microsoft/typescript-go/internal/transformers"
7+
)
8+
9+
type exponentiationTransformer struct {
10+
transformers.Transformer
11+
}
12+
13+
func (ch *exponentiationTransformer) visit(node *ast.Node) *ast.Node {
14+
return node // !!!
15+
}
16+
17+
func newExponentiationTransformer(emitContext *printer.EmitContext) *transformers.Transformer {
18+
tx := &exponentiationTransformer{}
19+
return tx.NewTransformer(tx.visit, emitContext)
20+
}

0 commit comments

Comments
 (0)