From 1083b4a9405b1e572fd4562bcac97bc1a25915dd Mon Sep 17 00:00:00 2001 From: jaxchow Date: Wed, 4 Nov 2020 11:04:33 +0000 Subject: [PATCH 1/6] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3Refeact-metadata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + internal/bundler/bundler_ts_test.go | 76 +++++++++++++++++++++++++++ internal/js_ast/js_ast.go | 15 +++--- internal/js_parser/js_parser.go | 4 +- internal/js_parser/js_parser_lower.go | 36 ++++++++++--- internal/runtime/runtime.go | 31 +++++++---- 6 files changed, 136 insertions(+), 27 deletions(-) diff --git a/.gitignore b/.gitignore index 5f5ad0871f6..0b48268c7c5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ /esbuild /esbuild.exe /github/ +/.devcontainer/ /npm/esbuild-darwin-64/bin/esbuild /npm/esbuild-freebsd-64/bin/esbuild /npm/esbuild-freebsd-arm64/bin/esbuild diff --git a/internal/bundler/bundler_ts_test.go b/internal/bundler/bundler_ts_test.go index 27b9aac4673..d534f3a0835 100644 --- a/internal/bundler/bundler_ts_test.go +++ b/internal/bundler/bundler_ts_test.go @@ -870,3 +870,79 @@ func TestExportTypeIssue379(t *testing.T) { }, }) } + +func TestTypeScriptDecoratorsMetadata(t *testing.T) { + ts_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/entry.ts": ` + import * as AF from './af' + console.log(AF) + `, + "/af.ts": ` + export function aaa(aa:AA1):void{ + const b:string = "1" + const {a} = aa; + } + export function bbb(bb:BB1):boolean{ + const {b} = bb + } + + `, + "/test.ts": ` + export type Test = Element + `, + }, + entryPaths: []string{"/entry.ts"}, + + options: config.Options{ + Mode: config.ModeBundle, + // OutputFormat: config.FormatCommonJS, + AbsOutputFile: "/out.js", + // UseRefleatMetaData + }, + }) +} + +func TestTypeScriptDecoratorsMetadataClass(t *testing.T) { + ts_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/entry.ts": ` + import * as AF from './af' + console.log(AF) + `, + "/af.ts": ` + class Abc1{} + + @Injectable + export default class Action { + constructor( + public readonly str:String="str", + public readonly middleware: MiddlewareFactory, + public readonly abc: Abc1, + public readonly def: DEF + ) { + // super(reducer, api, middleware); + } + @loading() + @param() + public fetchList(aa:string){} + } + + `, + "/metadata.ts": ` + export const __metadata=function(){} + `, + "/test.ts": ` + export type Test = Element + `, + }, + entryPaths: []string{"/entry.ts"}, + + options: config.Options{ + Mode: config.ModeBundle, + // OutputFormat: config.FormatCommonJS, + AbsOutputFile: "/out.js", + // UseRefleatMetaData + }, + }) +} diff --git a/internal/js_ast/js_ast.go b/internal/js_ast/js_ast.go index 01ebdad4ced..ea0787ce309 100644 --- a/internal/js_ast/js_ast.go +++ b/internal/js_ast/js_ast.go @@ -294,7 +294,7 @@ type Arg struct { TSDecorators []Expr Binding Binding Default *Expr - + Type Ref // "constructor(public x: boolean) {}" IsTypeScriptCtorField bool } @@ -1290,12 +1290,13 @@ type ScopeMember struct { } type Scope struct { - Kind ScopeKind - Parent *Scope - Children []*Scope - Members map[string]ScopeMember - Generated []Ref - + Kind ScopeKind + Parent *Scope + Children []*Scope + Members map[string]ScopeMember + Identifiers map[string]string + Generated []Ref + // ArgsIdentifier map[string]string // This is used to store the ref of the label symbol for ScopeLabel scopes. LabelRef Ref LabelStmtIsLoop bool diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 96cdfe5b791..b326f26d8e9 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -4106,12 +4106,12 @@ func (p *parser) parseFn(name *js_ast.LocRef, data fnOrArrowDataParse) (fn js_as value := p.parseExpr(js_ast.LComma) defaultValue = &value } - + argType := p.newSymbol(js_ast.SymbolHoisted, p.lexer.Identifier) fn.Args = append(fn.Args, js_ast.Arg{ TSDecorators: tsDecorators, Binding: arg, Default: defaultValue, - + Type: argType, // We need to track this because it affects code generation IsTypeScriptCtorField: isTypeScriptCtorField, }) diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index 33fb92fae44..7595fb37600 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -1629,7 +1629,21 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, // Generate a single call to "__decorate()" for this property if len(prop.TSDecorators) > 0 { loc := prop.Key.Loc - + prop.TSDecorators = append(prop.TSDecorators, + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:type")}}, + {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "Function")}}, + }), + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, + {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "[Object]")}}, + // {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, + }), + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:returntype")}}, + {Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "Promise")}}, + }), + ) // Clone the key for the property descriptor var descriptorKey js_ast.Expr switch k := keyExprNoSideEffects.Data.(type) { @@ -1644,10 +1658,10 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, } // This code tells "__decorate()" if the descriptor should be undefined - descriptorKind := float64(1) - if !prop.IsMethod { - descriptorKind = 2 - } + // descriptorKind := float64(1) + // if !prop.IsMethod { + // descriptorKind = 2 + // } // Instance properties use the prototype, static properties use the class var target js_ast.Expr @@ -1656,12 +1670,12 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, } else { target = js_ast.Expr{Loc: loc, Data: &js_ast.EDot{Target: nameFunc(), Name: "prototype", NameLoc: loc}} } - + // println(descriptorKey) decorator := p.callRuntime(loc, "__decorate", []js_ast.Expr{ {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, target, descriptorKey, - {Loc: loc, Data: &js_ast.ENumber{Value: descriptorKind}}, + {Loc: loc, Data: &js_ast.ENull{}}, }) // Static decorators are grouped after instance decorators @@ -1842,7 +1856,8 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, // Initialize TypeScript constructor parameter fields if p.TS.Parse { - for _, arg := range ctor.Fn.Args { + exprs := make([]js_ast.Expr, len(ctor.Fn.Args)) + for i, arg := range ctor.Fn.Args { if arg.IsTypeScriptCtorField { if id, ok := arg.Binding.Data.(*js_ast.BIdentifier); ok { parameterFields = append(parameterFields, js_ast.AssignStmt( @@ -1853,9 +1868,14 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, }}, js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: id.Ref}}, )) + exprs[i] = js_ast.Expr{Loc: arg.Binding.Loc, Data: &js_ast.EIdentifier{Ref: arg.Type}} } } } + class.TSDecorators = append(class.TSDecorators, p.callRuntime(classLoc, "__metadata", []js_ast.Expr{ + {Loc: classLoc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, + {Loc: classLoc, Data: &js_ast.EArray{Items: exprs}}, + })) } } } diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 87fe763fa81..614d9373d7b 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -121,16 +121,27 @@ func code(isES6 bool) string { // - kind === undefined: class // - kind === 1: method, parameter // - kind === 2: field - export var __decorate = (decorators, target, key, kind) => { - var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target - for (var i = decorators.length - 1, decorator; i >= 0; i--) - if (decorator = decorators[i]) - result = (kind ? decorator(target, key, result) : decorator(result)) || result - if (kind && result) - __defProp(target, key, result) - return result - } - export var __param = (index, decorator) => (target, key) => decorator(target, key, index) + // export var __decorate = (decorators, target, key, kind) => { + // var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target + // for (var i = decorators.length - 1, decorator; i >= 0; i--) + // if (decorator = decorators[i]) + // result = (kind ? decorator(target, key, result) : decorator(result)) || result + // if (kind && result) + // __defProp(target, key, result) + // return result + // } + // export var __param = (index, decorator) => (target, key) => decorator(target, key, index) + + export var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + + export var __metadata = (this && this.__param) || function (k, v) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); + }; // For class members export var __publicField = (obj, key, value) => { From 4d9c46b92b4f0ffdb6faa1698dfa29f8018b236b Mon Sep 17 00:00:00 2001 From: jaxchow Date: Thu, 5 Nov 2020 01:48:03 +0000 Subject: [PATCH 2/6] fix: add function returntype --- internal/js_ast/js_ast.go | 10 +++++----- internal/js_parser/js_parser.go | 4 ++++ internal/js_parser/js_parser_lower.go | 14 ++++++++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/internal/js_ast/js_ast.go b/internal/js_ast/js_ast.go index ea0787ce309..7875941b9d7 100644 --- a/internal/js_ast/js_ast.go +++ b/internal/js_ast/js_ast.go @@ -305,11 +305,11 @@ type Fn struct { Args []Arg Body FnBody ArgumentsRef Ref - - IsAsync bool - IsGenerator bool - HasRestArg bool - HasIfScope bool + ResultType Ref + IsAsync bool + IsGenerator bool + HasRestArg bool + HasIfScope bool } type FnBody struct { diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index b326f26d8e9..219c7cd6f9c 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -4141,6 +4141,10 @@ func (p *parser) parseFn(name *js_ast.LocRef, data fnOrArrowDataParse) (fn js_as if p.TS.Parse && p.lexer.Token == js_lexer.TColon { p.lexer.Next() p.skipTypeScriptReturnType() + println(p.lexer.Identifier) + if p.lexer.Identifier != "" { + fn.ResultType = p.newSymbol(js_ast.SymbolHoisted, p.lexer.Identifier) + } } // "function foo(): any;" diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index 7595fb37600..0331ebc2794 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -1639,11 +1639,17 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "[Object]")}}, // {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, }), - p.callRuntime(loc, "__metadata", []js_ast.Expr{ - {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:returntype")}}, - {Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "Promise")}}, - }), ) + fn := prop.Value.Data.(*js_ast.EFunction) + if fn.Fn.ResultType.InnerIndex != 0 { + prop.TSDecorators = append(prop.TSDecorators, + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:returntype")}}, + {Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: fn.Fn.ResultType}}, + }), + ) + } + // Clone the key for the property descriptor var descriptorKey js_ast.Expr switch k := keyExprNoSideEffects.Data.(type) { From 92b9c32545f63826fcb3f9d1994985b814a71341 Mon Sep 17 00:00:00 2001 From: jaxchow Date: Thu, 5 Nov 2020 03:13:18 +0000 Subject: [PATCH 3/6] feat: add esbuild [options] UseDecoratorMetadata --- internal/bundler/bundler.go | 22 ++++---- internal/config/config.go | 1 + internal/js_parser/js_parser.go | 2 +- internal/js_parser/js_parser_lower.go | 74 ++++++++++++++++----------- internal/resolver/resolver.go | 3 ++ internal/resolver/tsconfig_json.go | 8 +++ internal/runtime/runtime.go | 66 ++++++++++++++---------- lib/types.ts | 2 +- pkg/api/api_impl.go | 6 +++ 9 files changed, 116 insertions(+), 68 deletions(-) diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index 689403f75fe..019c7aeeada 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -1335,10 +1335,11 @@ func (b *Bundle) generateMetadataJSON(results []OutputFile, asciiOnly bool) []by } type runtimeCacheKey struct { - MangleSyntax bool - MinifyIdentifiers bool - ES6 bool - Platform config.Platform + MangleSyntax bool + MinifyIdentifiers bool + ES6 bool + UseDecoratorMetadata bool + Platform config.Platform } type runtimeCache struct { @@ -1354,17 +1355,18 @@ var globalRuntimeCache runtimeCache func (cache *runtimeCache) parseRuntime(options *config.Options) (source logger.Source, runtimeAST js_ast.AST, ok bool) { key := runtimeCacheKey{ // All configuration options that the runtime code depends on must go here - MangleSyntax: options.MangleSyntax, - MinifyIdentifiers: options.MinifyIdentifiers, - Platform: options.Platform, - ES6: runtime.CanUseES6(options.UnsupportedJSFeatures), + MangleSyntax: options.MangleSyntax, + MinifyIdentifiers: options.MinifyIdentifiers, + Platform: options.Platform, + UseDecoratorMetadata: options.UseDecoratorMetadata, + ES6: runtime.CanUseES6(options.UnsupportedJSFeatures), } // Determine which source to use if key.ES6 { - source = runtime.ES6Source + source = runtime.ES6Source(options) } else { - source = runtime.ES5Source + source = runtime.ES5Source(options) } // Cache hit? diff --git a/internal/config/config.go b/internal/config/config.go index f34e881d877..e22fdb1b29a 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -174,6 +174,7 @@ type Options struct { OmitRuntimeForTests bool PreserveUnusedImportsTS bool UseDefineForClassFields bool + UseDecoratorMetadata bool AvoidTDZ bool ASCIIOnly bool diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 219c7cd6f9c..14060d7ce82 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -4141,7 +4141,7 @@ func (p *parser) parseFn(name *js_ast.LocRef, data fnOrArrowDataParse) (fn js_as if p.TS.Parse && p.lexer.Token == js_lexer.TColon { p.lexer.Next() p.skipTypeScriptReturnType() - println(p.lexer.Identifier) + // println(p.lexer.Identifier) if p.lexer.Identifier != "" { fn.ResultType = p.newSymbol(js_ast.SymbolHoisted, p.lexer.Identifier) } diff --git a/internal/js_parser/js_parser_lower.go b/internal/js_parser/js_parser_lower.go index 0331ebc2794..899e992221a 100644 --- a/internal/js_parser/js_parser_lower.go +++ b/internal/js_parser/js_parser_lower.go @@ -1629,25 +1629,29 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, // Generate a single call to "__decorate()" for this property if len(prop.TSDecorators) > 0 { loc := prop.Key.Loc - prop.TSDecorators = append(prop.TSDecorators, - p.callRuntime(loc, "__metadata", []js_ast.Expr{ - {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:type")}}, - {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "Function")}}, - }), - p.callRuntime(loc, "__metadata", []js_ast.Expr{ - {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, - {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "[Object]")}}, - // {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, - }), - ) - fn := prop.Value.Data.(*js_ast.EFunction) - if fn.Fn.ResultType.InnerIndex != 0 { + if p.Options.UseDecoratorMetadata { + prop.TSDecorators = append(prop.TSDecorators, p.callRuntime(loc, "__metadata", []js_ast.Expr{ - {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:returntype")}}, - {Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: fn.Fn.ResultType}}, + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:type")}}, + {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "Function")}}, + }), + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, + {Loc: loc, Data: &js_ast.EIdentifier{Ref: p.newSymbol(js_ast.SymbolHoisted, "[Object]")}}, + // {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, }), ) + fn := prop.Value.Data.(*js_ast.EFunction) + if fn.Fn.ResultType.InnerIndex != 0 { + prop.TSDecorators = append(prop.TSDecorators, + p.callRuntime(loc, "__metadata", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:returntype")}}, + {Loc: prop.Key.Loc, Data: &js_ast.EIdentifier{Ref: fn.Fn.ResultType}}, + }), + ) + } + } // Clone the key for the property descriptor @@ -1664,10 +1668,10 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, } // This code tells "__decorate()" if the descriptor should be undefined - // descriptorKind := float64(1) - // if !prop.IsMethod { - // descriptorKind = 2 - // } + descriptorKind := float64(1) + if !prop.IsMethod { + descriptorKind = 2 + } // Instance properties use the prototype, static properties use the class var target js_ast.Expr @@ -1677,12 +1681,22 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, target = js_ast.Expr{Loc: loc, Data: &js_ast.EDot{Target: nameFunc(), Name: "prototype", NameLoc: loc}} } // println(descriptorKey) - decorator := p.callRuntime(loc, "__decorate", []js_ast.Expr{ - {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, - target, - descriptorKey, - {Loc: loc, Data: &js_ast.ENull{}}, - }) + var decorator js_ast.Expr + if p.Options.UseDecoratorMetadata { + decorator = p.callRuntime(loc, "__decorate", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, + target, + descriptorKey, + {Loc: loc, Data: &js_ast.ENull{}}, + }) + } else { + decorator = p.callRuntime(loc, "__decorate", []js_ast.Expr{ + {Loc: loc, Data: &js_ast.EArray{Items: prop.TSDecorators}}, + target, + descriptorKey, + {Loc: loc, Data: &js_ast.ENumber{Value: descriptorKind}}, + }) + } // Static decorators are grouped after instance decorators if prop.IsStatic { @@ -1878,10 +1892,12 @@ func (p *parser) lowerClass(stmt js_ast.Stmt, expr js_ast.Expr) ([]js_ast.Stmt, } } } - class.TSDecorators = append(class.TSDecorators, p.callRuntime(classLoc, "__metadata", []js_ast.Expr{ - {Loc: classLoc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, - {Loc: classLoc, Data: &js_ast.EArray{Items: exprs}}, - })) + if p.Options.UseDecoratorMetadata { + class.TSDecorators = append(class.TSDecorators, p.callRuntime(classLoc, "__metadata", []js_ast.Expr{ + {Loc: classLoc, Data: &js_ast.EString{Value: js_lexer.StringToUTF16("design:paramtypes")}}, + {Loc: classLoc, Data: &js_ast.EArray{Items: exprs}}, + })) + } } } } diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 78dea1ec73b..8f7304d0c45 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -89,6 +89,9 @@ type ResolveResult struct { // If true, the class field transform should use Object.defineProperty(). UseDefineForClassFieldsTS bool + // If true, the Decorator field transform should use refeact-metadata. + useDecoratorMetadata bool + // If true, unused imports are retained in TypeScript code. This matches the // behavior of the "importsNotUsedAsValues" field in "tsconfig.json" when the // value is not "remove". diff --git a/internal/resolver/tsconfig_json.go b/internal/resolver/tsconfig_json.go index 2634a4b166d..301f6c9d8c4 100644 --- a/internal/resolver/tsconfig_json.go +++ b/internal/resolver/tsconfig_json.go @@ -27,6 +27,7 @@ type TSConfigJSON struct { JSXFragmentFactory []string UseDefineForClassFields bool PreserveImportsNotUsedAsValues bool + UseDecoratorMetadata bool } func ParseTSConfigJSON( @@ -93,6 +94,13 @@ func ParseTSConfigJSON( } } + // Parse "useDecoratorMetadata" + if valueJSON, _, ok := getProperty(compilerOptionsJSON, "emitDecoratorMetadata"); ok { + if value, ok := getBool(valueJSON); ok { + result.UseDecoratorMetadata = value + } + } + // Parse "importsNotUsedAsValues" if valueJSON, _, ok := getProperty(compilerOptionsJSON, "importsNotUsedAsValues"); ok { if value, ok := getString(valueJSON); ok { diff --git a/internal/runtime/runtime.go b/internal/runtime/runtime.go index 614d9373d7b..bf4aa3912b2 100644 --- a/internal/runtime/runtime.go +++ b/internal/runtime/runtime.go @@ -7,6 +7,7 @@ package runtime import ( "github.com/evanw/esbuild/internal/compat" + "github.com/evanw/esbuild/internal/config" "github.com/evanw/esbuild/internal/logger" ) @@ -19,7 +20,7 @@ func CanUseES6(unsupportedFeatures compat.JSFeature) bool { return !unsupportedFeatures.Has(compat.Let) && !unsupportedFeatures.Has(compat.Arrow) } -func code(isES6 bool) string { +func code(isES6 bool, options *config.Options) string { // Note: The "__rest" function has a for-of loop which requires ES6, but // transforming destructuring to ES5 isn't even supported so it's ok. text := ` @@ -115,23 +116,29 @@ func code(isES6 bool) string { return __exportStar( __defProp(__create(__getProtoOf(module)), 'default', { value: module, enumerable: true }), module) - } + }` + if !options.UseDecoratorMetadata { + text += ` // For TypeScript decorators // - kind === undefined: class // - kind === 1: method, parameter // - kind === 2: field - // export var __decorate = (decorators, target, key, kind) => { - // var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target - // for (var i = decorators.length - 1, decorator; i >= 0; i--) - // if (decorator = decorators[i]) - // result = (kind ? decorator(target, key, result) : decorator(result)) || result - // if (kind && result) - // __defProp(target, key, result) - // return result - // } - // export var __param = (index, decorator) => (target, key) => decorator(target, key, index) - + export var __decorate = (decorators, target, key, kind) => { + var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target + for (var i = decorators.length - 1, decorator; i >= 0; i--) + if (decorator = decorators[i]) + result = (kind ? decorator(target, key, result) : decorator(result)) || result + if (kind && result) + __defProp(target, key, result) + return result + } + export var __param = (index, decorator) => (target, key) => decorator(target, key, index) + ` + + } else { + + text += ` export var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); @@ -141,8 +148,9 @@ func code(isES6 bool) string { export var __metadata = (this && this.__param) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); - }; - + };` + } + text += ` // For class members export var __publicField = (obj, key, value) => { if (typeof key !== 'symbol') key += '' @@ -213,20 +221,24 @@ func code(isES6 bool) string { return text } -var ES6Source = logger.Source{ - Index: SourceIndex, - KeyPath: logger.Path{Text: ""}, - PrettyPath: "", - IdentifierName: "runtime", - Contents: code(true /* isES6 */), +func ES6Source(options *config.Options) logger.Source { + return logger.Source{ + Index: SourceIndex, + KeyPath: logger.Path{Text: ""}, + PrettyPath: "", + IdentifierName: "runtime", + Contents: code(true /* isES6 */, options), + } } -var ES5Source = logger.Source{ - Index: SourceIndex, - KeyPath: logger.Path{Text: ""}, - PrettyPath: "", - IdentifierName: "runtime", - Contents: code(false /* isES6 */), +func ES5Source(options *config.Options) logger.Source { + return logger.Source{ + Index: SourceIndex, + KeyPath: logger.Path{Text: ""}, + PrettyPath: "", + IdentifierName: "runtime", + Contents: code(false /* isES6 */, options), + } } // The TypeScript decorator transform behaves similar to the official diff --git a/lib/types.ts b/lib/types.ts index 2cc791f5492..33555afeb2d 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -44,7 +44,7 @@ export interface BuildOptions extends CommonOptions { outExtension?: { [ext: string]: string }; publicPath?: string; inject?: string[]; - + useMetadata?: boolean; entryPoints?: string[]; stdin?: StdinOptions; plugins?: Plugin[]; diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index 6c8f10b158d..a927b44f5e1 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -665,6 +665,8 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult // Settings from the user come first preserveUnusedImportsTS := false useDefineForClassFieldsTS := false + useDecoratorMetadata := false + jsx := config.JSXOptions{ Factory: validateJSX(log, transformOpts.JSXFactory, "factory"), Fragment: validateJSX(log, transformOpts.JSXFragment, "fragment"), @@ -684,6 +686,9 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult if len(result.JSXFragmentFactory) > 0 { jsx.Fragment = result.JSXFragmentFactory } + if result.UseDecoratorMetadata { + useDecoratorMetadata = true + } if result.UseDefineForClassFields { useDefineForClassFieldsTS = true } @@ -718,6 +723,7 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult AbsOutputFile: transformOpts.Sourcefile + "-out", AvoidTDZ: transformOpts.AvoidTDZ, UseDefineForClassFields: useDefineForClassFieldsTS, + UseDecoratorMetadata: useDecoratorMetadata, PreserveUnusedImportsTS: preserveUnusedImportsTS, Stdin: &config.StdinInfo{ Loader: validateLoader(transformOpts.Loader), From 4adfa0a1870406ff703a79f9002d6a795232b302 Mon Sep 17 00:00:00 2001 From: jaxchow Date: Thu, 5 Nov 2020 08:30:19 +0000 Subject: [PATCH 4/6] feat: add cli --metadata --- cmd/esbuild/main.go | 3 +- internal/bundler/bundler_ts_test.go | 13 +++++--- pkg/api/api.go | 10 +++--- pkg/api/api_impl.go | 49 +++++++++++++++-------------- pkg/cli/cli_impl.go | 5 ++- 5 files changed, 45 insertions(+), 35 deletions(-) diff --git a/cmd/esbuild/main.go b/cmd/esbuild/main.go index 0cd14696b1a..195a194cdd5 100644 --- a/cmd/esbuild/main.go +++ b/cmd/esbuild/main.go @@ -59,7 +59,7 @@ Advanced options: --metafile=... Write metadata about the build to a JSON file --pure:N Mark the name N as a pure function for tree shaking --inject:F Import the file F into all input files and - automatically replace matching globals with imports + automatically replace matching globals with imports --tsconfig=... Use this tsconfig.json file instead of other ones --out-extension:.js=.mjs Use a custom output extension instead of ".js" --main-fields=... Override the main file order in package.json @@ -69,6 +69,7 @@ Advanced options: --color=... Force use of color terminal escapes (true | false) --charset=utf8 Do not escape UTF-8 code points --avoid-tdz An optimization for large bundles in Safari + --metadata support tsconfig emitDecoratorMetadata Examples: # Produces dist/entry_point.js and dist/entry_point.js.map diff --git a/internal/bundler/bundler_ts_test.go b/internal/bundler/bundler_ts_test.go index d534f3a0835..fdf8a4c98ad 100644 --- a/internal/bundler/bundler_ts_test.go +++ b/internal/bundler/bundler_ts_test.go @@ -917,15 +917,18 @@ func TestTypeScriptDecoratorsMetadataClass(t *testing.T) { export default class Action { constructor( public readonly str:String="str", - public readonly middleware: MiddlewareFactory, + public readonly middleware1: AA.MiddlewareFactory, public readonly abc: Abc1, public readonly def: DEF - ) { + ):void { // super(reducer, api, middleware); } @loading() @param() - public fetchList(aa:string){} + async fetchList(aa:string):Promise{ + // await aa + return aa + } } `, @@ -940,8 +943,10 @@ func TestTypeScriptDecoratorsMetadataClass(t *testing.T) { options: config.Options{ Mode: config.ModeBundle, + // OutputFormat: config.FormatCommonJS, - AbsOutputFile: "/out.js", + AbsOutputFile: "/out.js", + UseDecoratorMetadata: true, // UseRefleatMetaData }, }) diff --git a/pkg/api/api.go b/pkg/api/api.go index 6568ce77529..86a1bb8bd83 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -229,11 +229,11 @@ type BuildOptions struct { OutExtensions map[string]string PublicPath string Inject []string - - EntryPoints []string - Stdin *StdinOptions - Write bool - Plugins []Plugin + Metadata bool + EntryPoints []string + Stdin *StdinOptions + Write bool + Plugins []Plugin } type StdinOptions struct { diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index a927b44f5e1..c09013dc21e 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -468,29 +468,30 @@ func buildImpl(buildOpts BuildOptions) BuildResult { Factory: validateJSX(log, buildOpts.JSXFactory, "factory"), Fragment: validateJSX(log, buildOpts.JSXFragment, "fragment"), }, - Defines: validateDefines(log, buildOpts.Define, buildOpts.Pure), - Platform: validatePlatform(buildOpts.Platform), - SourceMap: validateSourceMap(buildOpts.Sourcemap), - MangleSyntax: buildOpts.MinifySyntax, - RemoveWhitespace: buildOpts.MinifyWhitespace, - MinifyIdentifiers: buildOpts.MinifyIdentifiers, - ASCIIOnly: validateASCIIOnly(buildOpts.Charset), - ModuleName: validateGlobalName(log, buildOpts.GlobalName), - CodeSplitting: buildOpts.Splitting, - OutputFormat: validateFormat(buildOpts.Format), - AbsOutputFile: validatePath(log, realFS, buildOpts.Outfile), - AbsOutputDir: validatePath(log, realFS, buildOpts.Outdir), - AbsOutputBase: validatePath(log, realFS, buildOpts.Outbase), - AbsMetadataFile: validatePath(log, realFS, buildOpts.Metafile), - OutputExtensions: validateOutputExtensions(log, buildOpts.OutExtensions), - ExtensionToLoader: validateLoaders(log, buildOpts.Loader), - ExtensionOrder: validateResolveExtensions(log, buildOpts.ResolveExtensions), - ExternalModules: validateExternals(log, realFS, buildOpts.External), - TsConfigOverride: validatePath(log, realFS, buildOpts.Tsconfig), - MainFields: buildOpts.MainFields, - PublicPath: buildOpts.PublicPath, - AvoidTDZ: buildOpts.AvoidTDZ, - InjectAbsPaths: make([]string, len(buildOpts.Inject)), + Defines: validateDefines(log, buildOpts.Define, buildOpts.Pure), + Platform: validatePlatform(buildOpts.Platform), + SourceMap: validateSourceMap(buildOpts.Sourcemap), + MangleSyntax: buildOpts.MinifySyntax, + RemoveWhitespace: buildOpts.MinifyWhitespace, + MinifyIdentifiers: buildOpts.MinifyIdentifiers, + ASCIIOnly: validateASCIIOnly(buildOpts.Charset), + ModuleName: validateGlobalName(log, buildOpts.GlobalName), + CodeSplitting: buildOpts.Splitting, + OutputFormat: validateFormat(buildOpts.Format), + AbsOutputFile: validatePath(log, realFS, buildOpts.Outfile), + AbsOutputDir: validatePath(log, realFS, buildOpts.Outdir), + AbsOutputBase: validatePath(log, realFS, buildOpts.Outbase), + AbsMetadataFile: validatePath(log, realFS, buildOpts.Metafile), + OutputExtensions: validateOutputExtensions(log, buildOpts.OutExtensions), + ExtensionToLoader: validateLoaders(log, buildOpts.Loader), + ExtensionOrder: validateResolveExtensions(log, buildOpts.ResolveExtensions), + ExternalModules: validateExternals(log, realFS, buildOpts.External), + TsConfigOverride: validatePath(log, realFS, buildOpts.Tsconfig), + MainFields: buildOpts.MainFields, + UseDecoratorMetadata: buildOpts.Metadata, + PublicPath: buildOpts.PublicPath, + AvoidTDZ: buildOpts.AvoidTDZ, + InjectAbsPaths: make([]string, len(buildOpts.Inject)), } for i, path := range buildOpts.Inject { options.InjectAbsPaths[i] = validatePath(log, realFS, path) @@ -687,7 +688,7 @@ func transformImpl(input string, transformOpts TransformOptions) TransformResult jsx.Fragment = result.JSXFragmentFactory } if result.UseDecoratorMetadata { - useDecoratorMetadata = true + useDecoratorMetadata = result.UseDecoratorMetadata } if result.UseDefineForClassFields { useDefineForClassFieldsTS = true diff --git a/pkg/cli/cli_impl.go b/pkg/cli/cli_impl.go index 944c249bda4..7d48144a6c7 100644 --- a/pkg/cli/cli_impl.go +++ b/pkg/cli/cli_impl.go @@ -346,7 +346,10 @@ func parseOptionsImpl(osArgs []string, buildOpts *api.BuildOptions, transformOpt } else { transformOpts.LogLevel = logLevel } - + case strings.HasPrefix(arg, "--metadata"): + if buildOpts != nil { + buildOpts.Metadata = true + } case !strings.HasPrefix(arg, "-") && buildOpts != nil: buildOpts.EntryPoints = append(buildOpts.EntryPoints, arg) From 2b977e1653db928316ad17bcee8d8c9391659540 Mon Sep 17 00:00:00 2001 From: jaxchow Date: Fri, 6 Nov 2020 03:20:51 +0000 Subject: [PATCH 5/6] chore: rename cli --metadata -> refleat-metadata --- cmd/esbuild/main.go | 2 +- pkg/cli/cli_impl.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/esbuild/main.go b/cmd/esbuild/main.go index 195a194cdd5..23afa81b6ad 100644 --- a/cmd/esbuild/main.go +++ b/cmd/esbuild/main.go @@ -69,7 +69,7 @@ Advanced options: --color=... Force use of color terminal escapes (true | false) --charset=utf8 Do not escape UTF-8 code points --avoid-tdz An optimization for large bundles in Safari - --metadata support tsconfig emitDecoratorMetadata + --reflect-metadata support tsconfig emitDecoratorMetadata Examples: # Produces dist/entry_point.js and dist/entry_point.js.map diff --git a/pkg/cli/cli_impl.go b/pkg/cli/cli_impl.go index 7d48144a6c7..da3e0f3aad8 100644 --- a/pkg/cli/cli_impl.go +++ b/pkg/cli/cli_impl.go @@ -346,7 +346,7 @@ func parseOptionsImpl(osArgs []string, buildOpts *api.BuildOptions, transformOpt } else { transformOpts.LogLevel = logLevel } - case strings.HasPrefix(arg, "--metadata"): + case strings.HasPrefix(arg, "--reflect-metadata"): if buildOpts != nil { buildOpts.Metadata = true } From fc8546a6666278257dfecbc62d03387701112fdd Mon Sep 17 00:00:00 2001 From: jaxchow Date: Fri, 6 Nov 2020 03:21:13 +0000 Subject: [PATCH 6/6] test: update snapshot --- internal/bundler/bundler_tsconfig_test.go | 27 +++++++++ internal/bundler/snapshots/snapshots_ts.txt | 60 +++++++++++++++++++ .../bundler/snapshots/snapshots_tsconfig.txt | 7 +++ 3 files changed, 94 insertions(+) diff --git a/internal/bundler/bundler_tsconfig_test.go b/internal/bundler/bundler_tsconfig_test.go index 823bb21a2df..56e6faa2a04 100644 --- a/internal/bundler/bundler_tsconfig_test.go +++ b/internal/bundler/bundler_tsconfig_test.go @@ -634,3 +634,30 @@ func TestTsconfigPreserveTypeImports(t *testing.T) { }, }) } + +func TestTsconfigEmitDecoratorMetadata(t *testing.T) { + tsconfig_suite.expectBundled(t, bundled{ + files: map[string]string{ + "/Users/user/project/src/entry.ts": ` + import {x, y} from "./foo" + console.log(1 as x) + `, + "/Users/user/project/src/tsconfig.json": `{ + "compilerOptions": { + "importsNotUsedAsValues": "preserve", + "emitDecoratorMetadata": true + } + }`, + }, + entryPaths: []string{"/Users/user/project/src/entry.ts"}, + options: config.Options{ + Mode: config.ModeBundle, + AbsOutputFile: "/Users/user/project/out.js", + ExternalModules: config.ExternalModules{ + AbsPaths: map[string]bool{ + "/Users/user/project/src/foo": true, + }, + }, + }, + }) +} diff --git a/internal/bundler/snapshots/snapshots_ts.txt b/internal/bundler/snapshots/snapshots_ts.txt index 821a7fed7b2..17cb48a9ea2 100644 --- a/internal/bundler/snapshots/snapshots_ts.txt +++ b/internal/bundler/snapshots/snapshots_ts.txt @@ -538,3 +538,63 @@ __decorate([ // /entry.js console.log(all_default, all_computed_default, a, b, c, d, e_default2, f_default, g_default2, h_default, i, j, k_default); + +================================================================================ +TestTypeScriptDecoratorsMetadata +---------- /out.js ---------- +// /af.ts +const af_exports = {}; +__export(af_exports, { + aaa: () => aaa, + bbb: () => bbb +}); +function aaa(aa) { + const b = "1"; + const {a} = aa; +} +function bbb(bb) { + const {b} = bb; +} + +// /entry.ts +console.log(af_exports); + +================================================================================ +TestTypeScriptDecoratorsMetadataClass +---------- /out.js ---------- +// /af.ts +const af_exports = {}; +__export(af_exports, { + default: () => af_default +}); +let Action = class { + constructor(str = "str", middleware1, abc, def) { + this.str = str; + this.middleware1 = middleware1; + this.abc = abc; + this.def = def; + } + async fetchList(aa) { + return aa; + } +}; +__decorate([ + loading(), + param(), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", Promise) +], Action.prototype, "fetchList", null); +Action = __decorate([ + Injectable, + __metadata("design:paramtypes", [ + String, + MiddlewareFactory, + Abc1, + DEF + ]) +], Action); +var af_default = Action; + +// /entry.ts +console.log(af_exports); diff --git a/internal/bundler/snapshots/snapshots_tsconfig.txt b/internal/bundler/snapshots/snapshots_tsconfig.txt index c47cfddea4d..32e7d92e8c6 100644 --- a/internal/bundler/snapshots/snapshots_tsconfig.txt +++ b/internal/bundler/snapshots/snapshots_tsconfig.txt @@ -94,6 +94,13 @@ var baseurl_nested_default = { // /Users/user/project/entry.ts console.log(baseurl_dot_default, baseurl_nested_default); +================================================================================ +TestTsconfigEmitDecoratorMetadata +---------- /Users/user/project/out.js ---------- +// /Users/user/project/src/entry.ts +import "./src/foo"; +console.log(1); + ================================================================================ TestTsconfigJsonBaseUrl ---------- /Users/user/project/out.js ----------