From e2a461f0fe3478d9c34e7a60e5da11044ff22f62 Mon Sep 17 00:00:00 2001 From: Evan Wallace Date: Sat, 17 Apr 2021 03:23:15 -0700 Subject: [PATCH] split "debug" log level into "debug" and "verbose" --- CHANGELOG.md | 4 + Makefile | 1 + cmd/esbuild/main.go | 4 +- internal/bundler/bundler.go | 30 ++++--- internal/bundler/bundler_default_test.go | 11 --- internal/js_parser/js_parser.go | 107 +++++++++++++++-------- internal/logger/logger.go | 62 +++++++++++-- internal/resolver/resolver.go | 42 +++++---- pkg/api/api.go | 1 + pkg/api/api_impl.go | 2 + pkg/cli/cli_impl.go | 4 +- scripts/end-to-end-tests.js | 2 +- 12 files changed, 180 insertions(+), 90 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 818b5cdcbfe..09d36c710a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -41,6 +41,10 @@ The TypeScript-to-JavaScript transform in esbuild has been updated to match the TypeScript compiler's output in both of these cases. +* Separate the `debug` log level into `debug` and `verbose` + + You can now use `--log-level=debug` to get some additional information that might indicate some problems with your build, but that has a high-enough false-positive rate that it isn't appropriate for warnings, which are on by default. Enabling the `debug` log level no longer generates a torrent of debug information like it did in the past; that behavior is now reserved for the `verbose` log level instead. + ## 0.11.11 * Initial support for Deno ([#936](https://github.com/evanw/esbuild/issues/936)) diff --git a/Makefile b/Makefile index 8d7a11edc54..6529c6ec30f 100644 --- a/Makefile +++ b/Makefile @@ -835,6 +835,7 @@ READMIN_ESBUILD_FLAGS += --define:global=window READMIN_ESBUILD_FLAGS += --loader:.js=jsx READMIN_ESBUILD_FLAGS += --minify READMIN_ESBUILD_FLAGS += --sourcemap +READMIN_ESBUILD_FLAGS += --log-level=debug bench-readmin-esbuild: esbuild | bench/readmin rm -fr bench/readmin/esbuild diff --git a/cmd/esbuild/main.go b/cmd/esbuild/main.go index d15b1c652dc..f9d861e25ef 100644 --- a/cmd/esbuild/main.go +++ b/cmd/esbuild/main.go @@ -63,8 +63,8 @@ var helpText = func(colors logger.Colors) string { --jsx-factory=... What to use for JSX instead of React.createElement --jsx-fragment=... What to use for JSX instead of React.Fragment --keep-names Preserve "name" on functions and classes - --log-level=... Disable logging (debug | info | warning | error | - silent, default info) + --log-level=... Disable logging (verbose | debug | info | warning | + error | silent, default info) --log-limit=... Maximum message count or 0 to disable (default 10) --main-fields=... Override the main file order in package.json (default "browser,module,main" when platform is diff --git a/internal/bundler/bundler.go b/internal/bundler/bundler.go index e7793d8bc63..01b6220a866 100644 --- a/internal/bundler/bundler.go +++ b/internal/bundler/bundler.go @@ -440,6 +440,10 @@ func parseFile(args parseArgs) { hint = fmt.Sprintf(" (the plugin %q didn't set a resolve directory)", pluginName) } debug.LogErrorMsg(args.log, &source, record.Range, fmt.Sprintf("Could not resolve %q%s", record.Path.Text, hint)) + } else if args.log.Level <= logger.LevelDebug && !didLogError && record.HandlesImportErrors { + args.log.AddRangeDebug(&source, record.Range, + fmt.Sprintf("Importing %q was allowed even though it could not be resolved because dynamic import failures appear to be handled here", + record.Path.Text)) } continue } @@ -538,15 +542,15 @@ func extractSourceMapFromComment( absPath := fs.Join(absResolveDir, comment.Text) path := logger.Path{Text: absPath, Namespace: "file"} contents, err, originalError := fsCache.ReadFile(fs, absPath) - if log.Debug && originalError != nil { - log.AddDebug(nil, logger.Loc{}, fmt.Sprintf("Failed to read file %q: %s", absPath, originalError.Error())) + if log.Level <= logger.LevelDebug && originalError != nil { + log.AddRangeDebug(source, comment.Range, fmt.Sprintf("Failed to read file %q: %s", res.PrettyPath(path), originalError.Error())) } if err != nil { if err == syscall.ENOENT { // Don't report a warning because this is likely unactionable return logger.Path{}, nil } - log.AddRangeError(source, comment.Range, fmt.Sprintf("Cannot read file %q: %s", res.PrettyPath(path), err.Error())) + log.AddRangeWarning(source, comment.Range, fmt.Sprintf("Cannot read file %q: %s", res.PrettyPath(path), err.Error())) return logger.Path{}, nil } return path, &contents @@ -833,7 +837,7 @@ func runOnLoadPlugins( absResolveDir: fs.Dir(source.KeyPath.Text), }, true } else { - if log.Debug && originalError != nil { + if log.Level <= logger.LevelDebug && originalError != nil { log.AddDebug(nil, logger.Loc{}, fmt.Sprintf("Failed to read file %q: %s", source.KeyPath.Text, originalError.Error())) } if err == syscall.ENOENT { @@ -935,8 +939,8 @@ func ScanBundle( options config.Options, ) Bundle { start := time.Now() - if log.Debug { - log.AddDebug(nil, logger.Loc{}, "Started the scan phase") + if log.Level <= logger.LevelVerbose { + log.AddVerbose(nil, logger.Loc{}, "Started the scan phase") } applyOptionDefaults(&options) @@ -973,8 +977,8 @@ func ScanBundle( s.scanAllDependencies() files := s.processScannedFiles() - if log.Debug { - log.AddDebug(nil, logger.Loc{}, fmt.Sprintf("Ended the scan phase (%dms)", time.Since(start).Milliseconds())) + if log.Level <= logger.LevelVerbose { + log.AddVerbose(nil, logger.Loc{}, fmt.Sprintf("Ended the scan phase (%dms)", time.Since(start).Milliseconds())) } return Bundle{ @@ -1262,7 +1266,7 @@ func (s *scanner) addEntryPoints(entryPoints []EntryPoint) []graph.EntryPoint { entryPoint.InputPath = "./" + entryPoint.InputPath } } - } else if s.log.Debug && originalError != nil { + } else if s.log.Level <= logger.LevelDebug && originalError != nil { s.log.AddDebug(nil, logger.Loc{}, fmt.Sprintf("Failed to read directory %q: %s", absPath, originalError.Error())) } } @@ -1828,8 +1832,8 @@ func applyOptionDefaults(options *config.Options) { func (b *Bundle) Compile(log logger.Log, options config.Options) ([]graph.OutputFile, string) { start := time.Now() - if log.Debug { - log.AddDebug(nil, logger.Loc{}, "Started the compile phase") + if log.Level <= logger.LevelVerbose { + log.AddVerbose(nil, logger.Loc{}, "Started the compile phase") } applyOptionDefaults(&options) @@ -1936,8 +1940,8 @@ func (b *Bundle) Compile(log logger.Log, options config.Options) ([]graph.Output outputFiles = outputFiles[:end] } - if log.Debug { - log.AddDebug(nil, logger.Loc{}, fmt.Sprintf("Ended the compile phase (%dms)", time.Since(start).Milliseconds())) + if log.Level <= logger.LevelVerbose { + log.AddVerbose(nil, logger.Loc{}, fmt.Sprintf("Ended the compile phase (%dms)", time.Since(start).Milliseconds())) } return outputFiles, metafileJSON diff --git a/internal/bundler/bundler_default_test.go b/internal/bundler/bundler_default_test.go index d95c58530df..e16de05f3a8 100644 --- a/internal/bundler/bundler_default_test.go +++ b/internal/bundler/bundler_default_test.go @@ -790,7 +790,6 @@ func TestRequireAndDynamicImportInvalidTemplate(t *testing.T) { require(tag` + "`./b`" + `) require(` + "`./${b}`" + `) - // Try/catch should silence this warning for require() try { require(tag` + "`./b`" + `) require(` + "`./${b}`" + `) @@ -803,7 +802,6 @@ func TestRequireAndDynamicImportInvalidTemplate(t *testing.T) { await import(tag` + "`./b`" + `) await import(` + "`./${b}`" + `) - // Try/catch should silence this warning for await import() try { import(tag` + "`./b`" + `) import(` + "`./${b}`" + `) @@ -819,9 +817,6 @@ func TestRequireAndDynamicImportInvalidTemplate(t *testing.T) { Mode: config.ModeBundle, AbsOutputFile: "/out.js", }, - expectedScanLog: `entry.js: warning: This call to "require" will not be bundled because the argument is not a string literal (surround with a try/catch to silence this warning) -entry.js: warning: This call to "require" will not be bundled because the argument is not a string literal (surround with a try/catch to silence this warning) -`, }) } @@ -905,8 +900,6 @@ func TestConditionalRequire(t *testing.T) { }, }, }, - expectedScanLog: `a.js: warning: This call to "require" will not be bundled because the argument is not a string literal (surround with a try/catch to silence this warning) -`, }) } @@ -944,7 +937,6 @@ func TestRequireBadArgumentCount(t *testing.T) { require() require("a", "b") - // Try/catch should silence this warning try { require() require("a", "b") @@ -957,9 +949,6 @@ func TestRequireBadArgumentCount(t *testing.T) { Mode: config.ModeBundle, AbsOutputFile: "/out.js", }, - expectedScanLog: `entry.js: warning: This call to "require" will not be bundled because it has 0 arguments (surround with a try/catch to silence this warning) -entry.js: warning: This call to "require" will not be bundled because it has 2 arguments (surround with a try/catch to silence this warning) -`, }) } diff --git a/internal/js_parser/js_parser.go b/internal/js_parser/js_parser.go index 1cbebb0467b..d74a5bbe02c 100644 --- a/internal/js_parser/js_parser.go +++ b/internal/js_parser/js_parser.go @@ -474,10 +474,6 @@ func (dc *duplicateCaseChecker) reset() { } func (dc *duplicateCaseChecker) check(p *parser, expr js_ast.Expr) { - if p.options.suppressWarningsAboutWeirdCode { - return - } - if hash, ok := duplicateCaseHash(expr); ok { bucket := hash % bloomFilterSize entry := &dc.bloomFilter[bucket/8] @@ -489,12 +485,14 @@ func (dc *duplicateCaseChecker) check(p *parser, expr js_ast.Expr) { if c.hash == hash { if equals, couldBeIncorrect := duplicateCaseEquals(c.value, expr); equals { r := p.source.RangeOfOperatorBefore(expr.Loc, "case") + text := "This case clause will never be evaluated because it duplicates an earlier case clause" if couldBeIncorrect { - p.log.AddRangeWarning(&p.source, r, - "This case clause may never be evaluated because it likely duplicates an earlier case clause") + text = "This case clause may never be evaluated because it likely duplicates an earlier case clause" + } + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) } else { - p.log.AddRangeWarning(&p.source, r, - "This case clause will never be evaluated because it duplicates an earlier case clause") + p.log.AddRangeDebug(&p.source, r, text) } } return @@ -9320,9 +9318,12 @@ func (p *parser) warnAboutTypeofAndString(a js_ast.Expr, b js_ast.Expr) { // Warn about typeof comparisons with values that will never be // returned. Here's an example of code with this problem: // https://github.com/olifolkerd/tabulator/issues/2962 + r := p.source.RangeOfString(b.Loc) + text := fmt.Sprintf("The \"typeof\" operator will never evaluate to %q", value) if !p.options.suppressWarningsAboutWeirdCode { - r := p.source.RangeOfString(b.Loc) - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf("The \"typeof\" operator will never evaluate to %q", value)) + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) } } } @@ -9352,10 +9353,6 @@ func maybeSimplifyEqualityComparison(e *js_ast.EBinary, isNotEqual bool) (js_ast } func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc logger.Loc) bool { - if p.options.suppressWarningsAboutWeirdCode { - return false - } - switch e := value.Data.(type) { case *js_ast.ENumber: // "0 === -0" is true in JavaScript. Here's an example of code with this @@ -9370,7 +9367,11 @@ func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc if op == "case" { text = "Comparison with -0 using a case clause will also match 0" } - p.log.AddRangeWarning(&p.source, r, text) + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) + } return true } @@ -9380,7 +9381,12 @@ func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc if op == "case" { text = "This case clause will never be evaluated because equality with NaN is always false" } - p.log.AddRangeWarning(&p.source, p.source.RangeOfOperatorBefore(afterOpLoc, op), text) + r := p.source.RangeOfOperatorBefore(afterOpLoc, op) + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) + } return true } @@ -9395,7 +9401,12 @@ func (p *parser) warnAboutEqualityCheck(op string, value js_ast.Expr, afterOpLoc if op == "case" { text = "This case clause will never be evaluated because the comparison is always false" } - p.log.AddRangeWarning(&p.source, p.source.RangeOfOperatorBefore(afterOpLoc, op), text) + r := p.source.RangeOfOperatorBefore(afterOpLoc, op) + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) + } return true } } @@ -10531,16 +10542,25 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO if !kind.IsPrivate() { r := logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} p.log.AddRangeError(&p.source, r, fmt.Sprintf("Private name %q must be declared in an enclosing class", name)) - } else if !p.options.suppressWarningsAboutWeirdCode { + } else { + var r logger.Range + var text string if in.assignTarget != js_ast.AssignTargetNone && (kind == js_ast.SymbolPrivateMethod || kind == js_ast.SymbolPrivateStaticMethod) { - r := logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf("Writing to read-only method %q will throw", name)) + r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} + text = fmt.Sprintf("Writing to read-only method %q will throw", name) } else if in.assignTarget != js_ast.AssignTargetNone && (kind == js_ast.SymbolPrivateGet || kind == js_ast.SymbolPrivateStaticGet) { - r := logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf("Writing to getter-only property %q will throw", name)) + r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} + text = fmt.Sprintf("Writing to getter-only property %q will throw", name) } else if in.assignTarget != js_ast.AssignTargetReplace && (kind == js_ast.SymbolPrivateSet || kind == js_ast.SymbolPrivateStaticSet) { - r := logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf("Reading from setter-only property %q will throw", name)) + r = logger.Range{Loc: e.Index.Loc, Len: int32(len(name))} + text = fmt.Sprintf("Reading from setter-only property %q will throw", name) + } + if text != "" { + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) + } } } @@ -10630,9 +10650,14 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO case *js_ast.EIdentifier: p.markStrictModeFeature(deleteBareName, js_lexer.RangeOfIdentifier(p.source, e.Value.Loc), "") } - if !p.options.suppressWarningsAboutWeirdCode && superPropLoc.Start != 0 { + if superPropLoc.Start != 0 { r := js_lexer.RangeOfIdentifier(p.source, superPropLoc) - p.log.AddRangeWarning(&p.source, r, "Attempting to delete a property of \"super\" will throw a ReferenceError") + text := "Attempting to delete a property of \"super\" will throw a ReferenceError" + if !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, r, text) + } else { + p.log.AddRangeDebug(&p.source, r, text) + } } p.deleteTarget = e.Value.Data @@ -11099,6 +11124,11 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO }} } + // Use a debug log so people can see this if they want to + r := js_lexer.RangeOfIdentifier(p.source, expr.Loc) + p.log.AddRangeDebug(&p.source, r, + "This \"import\" expression will not be bundled because the argument is not a string literal") + // We need to convert this into a call to "require()" if ES6 syntax is // not supported in the current output format. The full conversion: // @@ -11264,9 +11294,13 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO // and exported symbols due to scope hoisting. Except don't warn when // this code is in a 3rd-party library because there's nothing people // will be able to do about the warning. - if p.options.mode == config.ModeBundle && p.hasESModuleSyntax && !p.options.suppressWarningsAboutWeirdCode { - p.log.AddRangeWarning(&p.source, js_lexer.RangeOfIdentifier(p.source, e.Target.Loc), - "Using direct eval with a bundler is not recommended and may cause problems (more info: https://esbuild.github.io/link/direct-eval)") + if p.options.mode == config.ModeBundle { + text := "Using direct eval with a bundler is not recommended and may cause problems (more info: https://esbuild.github.io/link/direct-eval)" + if p.hasESModuleSyntax && !p.options.suppressWarningsAboutWeirdCode { + p.log.AddRangeWarning(&p.source, js_lexer.RangeOfIdentifier(p.source, e.Target.Loc), text) + } else { + p.log.AddRangeDebug(&p.source, js_lexer.RangeOfIdentifier(p.source, e.Target.Loc), text) + } } } } @@ -11344,11 +11378,10 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ERequire{ImportRecordIndex: importRecordIndex}} } - if !omitWarnings { - r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) - p.log.AddRangeWarning(&p.source, r, - "This call to \"require\" will not be bundled because the argument is not a string literal (surround with a try/catch to silence this warning)") - } + // Use a debug log so people can see this if they want to + r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) + p.log.AddRangeDebug(&p.source, r, + "This call to \"require\" will not be bundled because the argument is not a string literal") // Otherwise just return a clone of the "require()" call return js_ast.Expr{Loc: expr.Loc, Data: &js_ast.ECall{ @@ -11356,10 +11389,10 @@ func (p *parser) visitExprInOut(expr js_ast.Expr, in exprIn) (js_ast.Expr, exprO Args: []js_ast.Expr{arg}, }} }), exprOut{} - } else if !omitWarnings { + } else { r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) - p.log.AddRangeWarning(&p.source, r, fmt.Sprintf( - "This call to \"require\" will not be bundled because it has %d arguments (surround with a try/catch to silence this warning)", len(e.Args))) + text := fmt.Sprintf("This call to \"require\" will not be bundled because it has %d arguments", len(e.Args)) + p.log.AddRangeDebug(&p.source, r, text) } } else if p.options.outputFormat == config.FormatESModule && !omitWarnings { r := js_lexer.RangeOfIdentifier(p.source, e.Target.Loc) diff --git a/internal/logger/logger.go b/internal/logger/logger.go index 3692bee0806..8800e02c776 100644 --- a/internal/logger/logger.go +++ b/internal/logger/logger.go @@ -19,7 +19,7 @@ import ( const defaultTerminalWidth = 80 type Log struct { - Debug bool + Level LogLevel AddMsg func(Msg) HasErrors func() bool @@ -36,6 +36,7 @@ type LogLevel int8 const ( LevelNone LogLevel = iota + LevelVerbose LevelDebug LevelInfo LevelWarning @@ -50,6 +51,7 @@ const ( Warning Note Debug + Verbose ) func (kind MsgKind) String() string { @@ -62,6 +64,8 @@ func (kind MsgKind) String() string { return "note" case Debug: return "debug" + case Verbose: + return "verbose" default: panic("Internal error") } @@ -397,18 +401,30 @@ func NewStderrLog(options OutputOptions) Log { } return Log{ - Debug: options.LogLevel == LevelDebug, + Level: options.LogLevel, + AddMsg: func(msg Msg) { mutex.Lock() defer mutex.Unlock() msgs = append(msgs, msg) switch msg.Kind { + case Verbose: + if options.LogLevel <= LevelVerbose { + writeStringWithColor(os.Stderr, msg.String(options, terminalInfo)) + } + + case Debug: + if options.LogLevel <= LevelDebug { + writeStringWithColor(os.Stderr, msg.String(options, terminalInfo)) + } + case Error: hasErrors = true if options.LogLevel <= LevelError { errors++ } + case Warning: if options.LogLevel <= LevelWarning { warnings++ @@ -421,11 +437,6 @@ func NewStderrLog(options OutputOptions) Log { } switch msg.Kind { - case Debug: - if options.LogLevel <= LevelDebug { - writeStringWithColor(os.Stderr, msg.String(options, terminalInfo)) - } - case Error: if options.LogLevel <= LevelError { shownErrors++ @@ -449,17 +460,20 @@ func NewStderrLog(options OutputOptions) Log { } } }, + HasErrors: func() bool { mutex.Lock() defer mutex.Unlock() return hasErrors }, + AlmostDone: func() { mutex.Lock() defer mutex.Unlock() finalizeLog() }, + Done: func() []Msg { mutex.Lock() defer mutex.Unlock() @@ -779,7 +793,13 @@ func NewDeferLog() Log { var hasErrors bool return Log{ + Level: LevelInfo, + AddMsg: func(msg Msg) { + if msg.Kind == Verbose || msg.Kind == Debug { + // Ignore these when not writing to stderr + return + } mutex.Lock() defer mutex.Unlock() if msg.Kind == Error { @@ -787,13 +807,16 @@ func NewDeferLog() Log { } msgs = append(msgs, msg) }, + HasErrors: func() bool { mutex.Lock() defer mutex.Unlock() return hasErrors }, + AlmostDone: func() { }, + Done: func() []Msg { mutex.Lock() defer mutex.Unlock() @@ -889,6 +912,9 @@ func msgString(includeSource bool, terminalInfo TerminalInfo, kind MsgKind, data } switch kind { + case Verbose: + kindColor = colors.Green + case Debug: kindColor = colors.Blue @@ -1238,6 +1264,21 @@ func (log Log) AddDebugWithNotes(source *Source, loc Loc, text string, notes []M }) } +func (log Log) AddVerbose(source *Source, loc Loc, text string) { + log.AddMsg(Msg{ + Kind: Verbose, + Data: RangeData(source, Range{Loc: loc}, text), + }) +} + +func (log Log) AddVerboseWithNotes(source *Source, loc Loc, text string, notes []MsgData) { + log.AddMsg(Msg{ + Kind: Verbose, + Data: RangeData(source, Range{Loc: loc}, text), + Notes: notes, + }) +} + func (log Log) AddRangeError(source *Source, r Range, text string) { log.AddMsg(Msg{ Kind: Error, @@ -1252,6 +1293,13 @@ func (log Log) AddRangeWarning(source *Source, r Range, text string) { }) } +func (log Log) AddRangeDebug(source *Source, r Range, text string) { + log.AddMsg(Msg{ + Kind: Debug, + Data: RangeData(source, r, text), + }) +} + func (log Log) AddRangeErrorWithNotes(source *Source, r Range, text string, notes []MsgData) { log.AddMsg(Msg{ Kind: Error, diff --git a/internal/resolver/resolver.go b/internal/resolver/resolver.go index 211c9c358a1..ca083ae4b45 100644 --- a/internal/resolver/resolver.go +++ b/internal/resolver/resolver.go @@ -261,7 +261,7 @@ func NewResolver(fs fs.FS, log logger.Log, caches *cache.CacheSet, options confi func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.ImportKind) (*ResolveResult, DebugMeta) { r := resolverQuery{resolver: rr} - if r.log.Debug { + if r.log.Level <= logger.LevelDebug { r.debugLogs = &debugLogs{what: fmt.Sprintf( "Resolving import %q in directory %q of type %q", importPath, sourceDir, kind.StringForMetafile())} @@ -286,7 +286,7 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import r.debugLogs.addNote("Marking this path as implicitly external") } - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return &ResolveResult{ PathPair: PathPair{Primary: logger.Path{Text: importPath}}, IsExternal: true, @@ -300,7 +300,7 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import if r.debugLogs != nil { r.debugLogs.addNote("Putting this path in the \"dataurl\" namespace") } - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return &ResolveResult{ PathPair: PathPair{Primary: logger.Path{Text: importPath, Namespace: "dataurl"}}, }, DebugMeta{} @@ -310,7 +310,7 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import if r.debugLogs != nil { r.debugLogs.addNote("Marking this data URL as external") } - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return &ResolveResult{ PathPair: PathPair{Primary: logger.Path{Text: importPath}}, IsExternal: true, @@ -323,7 +323,7 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import if r.debugLogs != nil { r.debugLogs.addNote("Cannot resolve this path without a directory") } - r.flushDebugLogs() + r.flushDebugLogs(flushDueToFailure) return nil, DebugMeta{} } @@ -335,14 +335,14 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import // If resolution failed, try again with the URL query and/or hash removed suffix := strings.IndexAny(importPath, "?#") if suffix < 1 { - r.flushDebugLogs() + r.flushDebugLogs(flushDueToFailure) return nil, debug } if r.debugLogs != nil { r.debugLogs.addNote(fmt.Sprintf("Retrying resolution after removing the suffix %q", importPath[suffix:])) } if result2, debug2 := r.resolveWithoutSymlinks(sourceDir, importPath[:suffix], kind); result2 == nil { - r.flushDebugLogs() + r.flushDebugLogs(flushDueToFailure) return nil, debug } else { result = result2 @@ -356,7 +356,7 @@ func (rr *resolver) Resolve(sourceDir string, importPath string, kind ast.Import // If successful, resolve symlinks using the directory info cache r.finalizeResolve(result) - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return result, debug } @@ -373,7 +373,7 @@ func (r resolverQuery) isExternalPattern(path string) bool { func (rr *resolver) ResolveAbs(absPath string) *ResolveResult { r := resolverQuery{resolver: rr} - if r.log.Debug { + if r.log.Level <= logger.LevelDebug { r.debugLogs = &debugLogs{what: fmt.Sprintf("Getting metadata for absolute path %s", absPath)} } @@ -383,16 +383,12 @@ func (rr *resolver) ResolveAbs(absPath string) *ResolveResult { // Just decorate the absolute path with information from parent directories result := &ResolveResult{PathPair: PathPair{Primary: logger.Path{Text: absPath, Namespace: "file"}}} r.finalizeResolve(result) - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return result } func (rr *resolver) ProbeResolvePackageAsRelative(sourceDir string, importPath string, kind ast.ImportKind) *ResolveResult { r := resolverQuery{resolver: rr} - if r.log.Debug { - r.debugLogs = &debugLogs{what: fmt.Sprintf("Probing for relative import %q in directory %q", importPath, sourceDir)} - } - absPath := r.fs.Join(sourceDir, importPath) r.mutex.Lock() @@ -401,11 +397,10 @@ func (rr *resolver) ProbeResolvePackageAsRelative(sourceDir string, importPath s if pair, ok, diffCase := r.loadAsFileOrDirectory(absPath, kind); ok { result := &ResolveResult{PathPair: pair, DifferentCase: diffCase} r.finalizeResolve(result) - r.flushDebugLogs() + r.flushDebugLogs(flushDueToSuccess) return result } - r.flushDebugLogs() return nil } @@ -430,9 +425,20 @@ func (d *debugLogs) decreaseIndent() { d.indent = d.indent[2:] } -func (r resolverQuery) flushDebugLogs() { +type flushMode uint8 + +const ( + flushDueToFailure flushMode = iota + flushDueToSuccess +) + +func (r resolverQuery) flushDebugLogs(mode flushMode) { if r.debugLogs != nil { - r.log.AddDebugWithNotes(nil, logger.Loc{}, r.debugLogs.what, r.debugLogs.notes) + if mode == flushDueToFailure { + r.log.AddDebugWithNotes(nil, logger.Loc{}, r.debugLogs.what, r.debugLogs.notes) + } else if r.log.Level <= logger.LevelVerbose { + r.log.AddVerboseWithNotes(nil, logger.Loc{}, r.debugLogs.what, r.debugLogs.notes) + } } } diff --git a/pkg/api/api.go b/pkg/api/api.go index c44fd499785..a4b18858415 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -195,6 +195,7 @@ type LogLevel uint8 const ( LogLevelSilent LogLevel = iota + LogLevelVerbose LogLevelDebug LogLevelInfo LogLevelWarning diff --git a/pkg/api/api_impl.go b/pkg/api/api_impl.go index 239d5ce3d93..75837f61068 100644 --- a/pkg/api/api_impl.go +++ b/pkg/api/api_impl.go @@ -149,6 +149,8 @@ func validateColor(value StderrColor) logger.UseColor { func validateLogLevel(value LogLevel) logger.LogLevel { switch value { + case LogLevelVerbose: + return logger.LevelVerbose case LogLevelDebug: return logger.LevelDebug case LogLevelInfo: diff --git a/pkg/cli/cli_impl.go b/pkg/cli/cli_impl.go index d4278c4d07f..c125de53ac7 100644 --- a/pkg/cli/cli_impl.go +++ b/pkg/cli/cli_impl.go @@ -437,6 +437,8 @@ func parseOptionsImpl( value := arg[len("--log-level="):] var logLevel api.LogLevel switch value { + case "verbose": + logLevel = api.LogLevelVerbose case "debug": logLevel = api.LogLevelDebug case "info": @@ -448,7 +450,7 @@ func parseOptionsImpl( case "silent": logLevel = api.LogLevelSilent default: - return fmt.Errorf("Invalid log level: %q (valid: debug, info, warning, error, silent)", arg), nil + return fmt.Errorf("Invalid log level: %q (valid: verbose, debug, info, warning, error, silent)", arg), nil } if buildOpts != nil { buildOpts.LogLevel = logLevel diff --git a/scripts/end-to-end-tests.js b/scripts/end-to-end-tests.js index 569416f26e3..953d680e662 100644 --- a/scripts/end-to-end-tests.js +++ b/scripts/end-to-end-tests.js @@ -3648,7 +3648,7 @@ `, 'src/entry.js.map/x': ``, }, { - expectedStderr: ` > src/entry.js:2:29: error: Cannot read file "src/entry.js.map": ${errorText} + expectedStderr: ` > src/entry.js:2:29: warning: Cannot read file "src/entry.js.map": ${errorText} 2 │ //# sourceMappingURL=entry.js.map ╵ ~~~~~~~~~~~~