From 4ab6a6de37d7f0588d5a7f513d45654d328797d6 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Wed, 10 Feb 2021 20:59:09 -0800 Subject: [PATCH] suggest default import when calling import ns --- internal/bundler/bundler_default_test.go | 6 ++- internal/js_parser/js_parser.go | 21 ++++++-- internal/logger/logger.go | 62 +++++++++++++++++------- 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/internal/bundler/bundler_default_test.go b/internal/bundler/bundler_default_test.go index 11fe1ca6c85..c72c77ae984 100644 --- a/internal/bundler/bundler_default_test.go +++ b/internal/bundler/bundler_default_test.go @@ -3905,9 +3905,11 @@ func TestCallImportNamespaceWarning(t *testing.T) { AbsOutputDir: "/out", OutputFormat: config.FormatESModule, }, - expectedScanLog: `js.js: warning: Cannot call "a" because it's an import namespace object, not a function + expectedScanLog: `js.js: warning: Calling "a" will crash at run-time because it's an import namespace object, not a function +js.js: note: Consider changing "a" to a default import instead js.js: warning: Cannot construct "a" because it's an import namespace object, not a function -ts.ts: warning: Cannot call "a" because it's an import namespace object, not a function (make sure to enable TypeScript's "esModuleInterop" setting) +ts.ts: warning: Calling "a" will crash at run-time because it's an import namespace object, not a function (make sure to enable TypeScript's "esModuleInterop" setting) +ts.ts: note: Consider changing "a" to a default import instead ts.ts: warning: Cannot construct "a" because it's an import namespace object, not a function (make sure to enable TypeScript's "esModuleInterop" setting) `, }) diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 384c18b74d3..2eb44da78a7 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -10813,9 +10813,24 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO if p.options.ts.Parse { hint = " (make sure to enable TypeScript's \"esModuleInterop\" setting)" } - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf( - "Cannot call %q because it's an import namespace object, not a function%s", - p.symbols[id.Ref.InnerIndex].OriginalName, hint)) + var notes []logger.MsgData + name := p.symbols[id.Ref.InnerIndex].OriginalName + if member, ok := p.moduleScope.Members[name]; ok && member.Ref == id.Ref { + if star := p.source.RangeOfOperatorBefore(member.Loc, "*"); star.Len > 0 { + if as := p.source.RangeOfOperatorBefore(member.Loc, "as"); as.Len > 0 && as.Loc.Start > star.Loc.Start { + note := logger.RangeData(&p.source, + logger.Range{Loc: star.Loc, Len: js_lexer.RangeOfIdentifier(p.source, member.Loc).End() - star.Loc.Start}, + fmt.Sprintf("Consider changing %q to a default import instead", name)) + note.Location.Suggestion = name + notes = []logger.MsgData{note} + } + } + } + p.log.AddRangeWarningWithNotes(&p.source, r, fmt.Sprintf( + "Calling %q will crash at run-time because it's an import namespace object, not a function%s", + p.symbols[id.Ref.InnerIndex].OriginalName, hint), + notes, + ) } } diff --git a/internal/logger/logger.go b/internal/logger/logger.go index d0d0e29a957..7938598c686 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -70,12 +70,13 @@ type MsgData struct { } type MsgLocation struct { - File string - Namespace string - Line int // 1-based - Column int // 0-based, in bytes - Length int // in bytes - LineText string + File string + Namespace string + Line int // 1-based + Column int // 0-based, in bytes + Length int // in bytes + LineText string + Suggestion string } type Loc struct { @@ -757,8 +758,12 @@ func marginWithLineText(maxMargin int, line int) string { return fmt.Sprintf(" %s%s │ ", strings.Repeat(" ", maxMargin-len(number)), number) } -func emptyMarginText(maxMargin int) string { - return fmt.Sprintf(" %s ╵ ", strings.Repeat(" ", maxMargin)) +func emptyMarginText(maxMargin int, isLast bool) string { + space := strings.Repeat(" ", maxMargin) + if isLast { + return fmt.Sprintf(" %s ╵ ", space) + } + return fmt.Sprintf(" %s │ ", space) } func msgString(options OutputOptions, terminalInfo TerminalInfo, kind MsgKind, data MsgData, maxMargin int) string { @@ -817,19 +822,40 @@ func msgString(options OutputOptions, terminalInfo TerminalInfo, kind MsgKind, d d := detailStruct(data, terminalInfo, maxMargin) if terminalInfo.UseColorEscapes { - return fmt.Sprintf("%s%s%s: %s%s: %s%s\n%s%s%s%s%s%s\n%s%s%s%s%s%s\n", + if d.Suggestion != "" { + return fmt.Sprintf("%s%s%s: %s%s: %s%s\n%s%s%s%s%s%s\n%s%s%s%s%s\n%s%s%s%s%s%s%s\n", + textColor, textIndent, d.Path, + kindColor, kind.String(), + textResetColor, d.Message, + colorResetDim, d.SourceBefore, colorGreen, d.SourceMarked, colorResetDim, d.SourceAfter, + emptyMarginText(maxMargin, false), d.Indent, colorGreen, d.Marker, colorResetDim, + emptyMarginText(maxMargin, true), d.Indent, colorGreen, d.Suggestion, colorResetDim, + d.ContentAfter, colorReset) + } + + return fmt.Sprintf("%s%s%s: %s%s: %s%s\n%s%s%s%s%s%s\n%s%s%s%s%s%s%s\n", textColor, textIndent, d.Path, kindColor, kind.String(), textResetColor, d.Message, colorResetDim, d.SourceBefore, colorGreen, d.SourceMarked, colorResetDim, d.SourceAfter, - d.Indent, colorGreen, d.Marker, - colorResetDim, d.ContentAfter, colorReset) + emptyMarginText(maxMargin, true), d.Indent, colorGreen, d.Marker, colorResetDim, + d.ContentAfter, colorReset) + } + + if d.Suggestion != "" { + return fmt.Sprintf("%s%s: %s: %s\n%s%s%s\n%s%s%s\n%s%s%s%s\n", + textIndent, d.Path, kind.String(), d.Message, + d.SourceBefore, d.SourceMarked, d.SourceAfter, + emptyMarginText(maxMargin, false), d.Indent, d.Marker, + emptyMarginText(maxMargin, true), d.Indent, d.Suggestion, + d.ContentAfter) } - return fmt.Sprintf("%s%s: %s: %s\n%s%s%s\n%s%s%s\n", + return fmt.Sprintf("%s%s: %s: %s\n%s%s%s\n%s%s%s%s\n", textIndent, d.Path, kind.String(), d.Message, d.SourceBefore, d.SourceMarked, d.SourceAfter, - d.Indent, d.Marker, d.ContentAfter) + emptyMarginText(maxMargin, true), d.Indent, d.Marker, + d.ContentAfter) } type MsgDetail struct { @@ -842,8 +868,9 @@ type MsgDetail struct { SourceMarked string SourceAfter string - Indent string - Marker string + Indent string + Marker string + Suggestion string ContentAfter string } @@ -1038,8 +1065,9 @@ func detailStruct(data MsgData, terminalInfo TerminalInfo, maxMargin int) MsgDet SourceMarked: lineText[markerStart:markerEnd], SourceAfter: lineText[markerEnd:], - Indent: emptyMarginText(maxMargin) + indent, - Marker: marker, + Indent: indent, + Marker: marker, + Suggestion: loc.Suggestion, ContentAfter: afterFirstLine, }