diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-05-15 11:40:34 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2022-05-15 20:25:25 +0200 |
commit | fc9f315d86e1fe51c3d1eec3b60680113b2e3aa6 (patch) | |
tree | 69e8ffc4d84e8f02e0e6f098c27cda9dd3bcc544 /resources | |
parent | 4b189d8fd93d3fa326b31d451d5594c917e6c714 (diff) |
Improve SASS errors
Fixes #9897
Diffstat (limited to 'resources')
9 files changed, 179 insertions, 53 deletions
diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go index 34dae4666..23e28f675 100644 --- a/resources/resource_transformers/js/build.go +++ b/resources/resource_transformers/js/build.go @@ -165,7 +165,7 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx if err == nil { fe := herrors. - NewFileError(errors.New(errorMessage), path). + NewFileErrorFromName(errors.New(errorMessage), path). UpdatePosition(text.Position{Offset: -1, LineNumber: loc.Line, ColumnNumber: loc.Column}). UpdateContent(f, nil) diff --git a/resources/resource_transformers/postcss/integration_test.go b/resources/resource_transformers/postcss/integration_test.go index 69f0964d0..fdebcc52c 100644 --- a/resources/resource_transformers/postcss/integration_test.go +++ b/resources/resource_transformers/postcss/integration_test.go @@ -183,6 +183,6 @@ func TestTransformPostCSSImporSkipInlineImportsNotFound(t *testing.T) { TxtarString: files, }).Build() - s.AssertFileContent("public/css/styles.css", filepath.FromSlash(`@import "components/doesnotexist.css";`)) + s.AssertFileContent("public/css/styles.css", `@import "components/doesnotexist.css";`) } diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go index 325dc1ec2..ed08f7202 100644 --- a/resources/resource_transformers/postcss/postcss.go +++ b/resources/resource_transformers/postcss/postcss.go @@ -314,7 +314,7 @@ func (imp *importResolver) importRecursive( LineNumber: offset + 1, ColumnNumber: column + 1, } - return 0, "", herrors.NewFileErrorFromFileInPos(fmt.Errorf("failed to resolve CSS @import %q", filename), pos, imp.fs, nil) + return 0, "", herrors.NewFileErrorFromFileInPos(fmt.Errorf("failed to resolve CSS @import \"%s\"", filename), pos, imp.fs, nil) } i-- @@ -421,7 +421,7 @@ func (imp *importResolver) toFileError(output string) error { } defer f.Close() - ferr := herrors.NewFileError(inErr, realFilename) + ferr := herrors.NewFileErrorFromName(inErr, realFilename) pos := ferr.Position() pos.LineNumber = file.Offset + 1 return ferr.UpdatePosition(pos).UpdateContent(f, nil) diff --git a/resources/resource_transformers/tocss/dartsass/client.go b/resources/resource_transformers/tocss/dartsass/client.go index fb794f4e8..7c3a7ecba 100644 --- a/resources/resource_transformers/tocss/dartsass/client.go +++ b/resources/resource_transformers/tocss/dartsass/client.go @@ -20,7 +20,9 @@ import ( "io" "strings" + "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/helpers" + "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/hugolib/filesystems" "github.com/gohugoio/hugo/resources" "github.com/gohugoio/hugo/resources/resource" @@ -33,6 +35,10 @@ import ( // used as part of the cache key. const transformationName = "tocss-dart" +// See https://github.com/sass/dart-sass-embedded/issues/24 +// Note: This prefix must be all lower case. +const dartSassStdinPrefix = "hugostdin:" + func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error) { if !Supports() { return &Client{dartSassNotAvailable: true}, nil @@ -44,7 +50,7 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) (*Client, error) transpiler, err := godartsass.Start(godartsass.Options{ LogEventHandler: func(event godartsass.LogEvent) { - message := strings.ReplaceAll(event.Message, stdinPrefix, "") + message := strings.ReplaceAll(event.Message, dartSassStdinPrefix, "") switch event.Type { case godartsass.LogEventTypeDebug: // Log as Info for now, we may adjust this if it gets too chatty. @@ -94,7 +100,7 @@ func (c *Client) toCSS(args godartsass.Args, src io.Reader) (godartsass.Result, if err.Error() == "unexpected EOF" { return res, fmt.Errorf("got unexpected EOF when executing %q. The user running hugo must have read and execute permissions on this program. With execute permissions only, this error is thrown.", dartSassEmbeddedBinaryName) } - return res, err + return res, herrors.NewFileErrorFromFileInErr(err, hugofs.Os, herrors.OffsetMatcher) } return res, err diff --git a/resources/resource_transformers/tocss/dartsass/integration_test.go b/resources/resource_transformers/tocss/dartsass/integration_test.go index a1ac1d59f..c127057a5 100644 --- a/resources/resource_transformers/tocss/dartsass/integration_test.go +++ b/resources/resource_transformers/tocss/dartsass/integration_test.go @@ -14,8 +14,10 @@ package dartsass_test import ( + "strings" "testing" + qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/hugolib" "github.com/gohugoio/hugo/resources/resource_transformers/tocss/dartsass" jww "github.com/spf13/jwalterweatherman" @@ -196,3 +198,76 @@ T1: {{ $r.Content }} b.AssertLogMatches(`INFO.*Dart Sass: .*assets.*main.scss:1:0: bar`) } + +func TestTransformErrors(t *testing.T) { + if !dartsass.Supports() { + t.Skip() + } + + c := qt.New(t) + + const filesTemplate = ` +-- config.toml -- +-- assets/scss/components/_foo.scss -- +/* comment line 1 */ +$foocolor: #ccc; + +foo { + color: $foocolor; +} +-- assets/scss/main.scss -- +/* comment line 1 */ +/* comment line 2 */ +@import "components/foo"; +/* comment line 4 */ + + $maincolor: #eee; + +body { + color: $maincolor; +} + +-- layouts/index.html -- +{{ $cssOpts := dict "transpiler" "dartsass" }} +{{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts | minify }} +T1: {{ $r.Content }} + + ` + + c.Run("error in main", func(c *qt.C) { + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: c, + TxtarString: strings.Replace(filesTemplate, "$maincolor: #eee;", "$maincolor #eee;", 1), + NeedsOsFS: true, + }).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `main.scss:8:13":`) + b.Assert(err.Error(), qt.Contains, `: expected ":".`) + fe := b.AssertIsFileError(err) + b.Assert(fe.ErrorContext(), qt.IsNotNil) + b.Assert(fe.ErrorContext().Lines, qt.DeepEquals, []string{" $maincolor #eee;", "", "body {", "\tcolor: $maincolor;", "}"}) + b.Assert(fe.ErrorContext().ChromaLexer, qt.Equals, "scss") + + }) + + c.Run("error in import", func(c *qt.C) { + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: c, + TxtarString: strings.Replace(filesTemplate, "$foocolor: #ccc;", "$foocolor #ccc;", 1), + NeedsOsFS: true, + }).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, `_foo.scss:2:10":`) + b.Assert(err.Error(), qt.Contains, `: expected ":".`) + fe := b.AssertIsFileError(err) + b.Assert(fe.ErrorContext(), qt.IsNotNil) + b.Assert(fe.ErrorContext().Lines, qt.DeepEquals, []string{"/* comment line 1 */", "$foocolor #ccc;", "", "foo {"}) + b.Assert(fe.ErrorContext().ChromaLexer, qt.Equals, "scss") + + }) + +} diff --git a/resources/resource_transformers/tocss/dartsass/transform.go b/resources/resource_transformers/tocss/dartsass/transform.go index ba3517e26..9d17d3bcc 100644 --- a/resources/resource_transformers/tocss/dartsass/transform.go +++ b/resources/resource_transformers/tocss/dartsass/transform.go @@ -16,13 +16,12 @@ package dartsass import ( "fmt" "io" - "net/url" "path" "path/filepath" "strings" - "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/common/hexec" + "github.com/gohugoio/hugo/common/paths" "github.com/gohugoio/hugo/htesting" "github.com/gohugoio/hugo/media" @@ -38,9 +37,6 @@ import ( ) const ( - // See https://github.com/sass/dart-sass-embedded/issues/24 - // Note: This prefix must be all lower case. - stdinPrefix = "hugostdin:" dartSassEmbeddedBinaryName = "dart-sass-embedded" ) @@ -76,7 +72,7 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error { } baseDir := path.Dir(ctx.SourcePath) - filename := stdinPrefix + filename := dartSassStdinPrefix if ctx.SourcePath != "" { filename += t.c.sfs.RealFilename(ctx.SourcePath) @@ -108,26 +104,6 @@ func (t *transform) Transform(ctx *resources.ResourceTransformationCtx) error { res, err := t.c.toCSS(args, ctx.From) if err != nil { - if sassErr, ok := err.(godartsass.SassError); ok { - start := sassErr.Span.Start - context := strings.TrimSpace(sassErr.Span.Context) - filename, _ := urlToFilename(sassErr.Span.Url) - if strings.HasPrefix(filename, stdinPrefix) { - filename = filename[len(stdinPrefix):] - } - - offsetMatcher := func(m herrors.LineMatcher) int { - if m.Offset+len(m.Line) >= start.Offset && strings.Contains(m.Line, context) { - // We found the line, but return 0 to signal that we want to determine - // the column from the error. - return 0 - } - return -1 - } - - return herrors.NewFileErrorFromFile(sassErr, filename, hugofs.Os, offsetMatcher) - - } return err } @@ -154,7 +130,7 @@ type importResolver struct { } func (t importResolver) CanonicalizeURL(url string) (string, error) { - filePath, isURL := urlToFilename(url) + filePath, isURL := paths.UrlToFilename(url) var prevDir string var pathDir string if isURL { @@ -200,23 +176,7 @@ func (t importResolver) CanonicalizeURL(url string) (string, error) { } func (t importResolver) Load(url string) (string, error) { - filename, _ := urlToFilename(url) + filename, _ := paths.UrlToFilename(url) b, err := afero.ReadFile(hugofs.Os, filename) return string(b), err } - -// TODO(bep) add tests -func urlToFilename(urls string) (string, bool) { - u, err := url.ParseRequestURI(urls) - if err != nil { - return filepath.FromSlash(urls), false - } - p := filepath.FromSlash(u.Path) - - if u.Host != "" { - // C:\data\file.txt - p = strings.ToUpper(u.Host) + ":" + p - } - - return p, true -} diff --git a/resources/resource_transformers/tocss/scss/client_extended.go b/resources/resource_transformers/tocss/scss/client_extended.go index c9b347c4c..bfee39499 100644 --- a/resources/resource_transformers/tocss/scss/client_extended.go +++ b/resources/resource_transformers/tocss/scss/client_extended.go @@ -47,6 +47,7 @@ func (c *Client) ToCSS(res resources.ResourceTransformer, opts Options) (resourc } return res.Transform(&toCSSTransformation{c: c, options: internalOptions}) + } type toCSSTransformation struct { diff --git a/resources/resource_transformers/tocss/scss/integration_test.go b/resources/resource_transformers/tocss/scss/integration_test.go index cbc7e192b..72c0fd988 100644 --- a/resources/resource_transformers/tocss/scss/integration_test.go +++ b/resources/resource_transformers/tocss/scss/integration_test.go @@ -14,6 +14,8 @@ package scss_test import ( + "path/filepath" + "strings" "testing" qt "github.com/frankban/quicktest" @@ -133,7 +135,7 @@ moo { -- config.toml -- theme = 'mytheme' -- layouts/index.html -- -{{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) "transpiler" "dartsass" ) }} +{{ $cssOpts := (dict "includePaths" (slice "node_modules/foo" ) ) }} {{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts | minify }} T1: {{ $r.Content }} -- themes/mytheme/assets/scss/components/_boo.scss -- @@ -171,3 +173,75 @@ zoo { b.AssertFileContent("public/index.html", `T1: moo{color:#ccc}boo{color:green}zoo{color:pink}`) } + +func TestTransformErrors(t *testing.T) { + if !scss.Supports() { + t.Skip() + } + + c := qt.New(t) + + const filesTemplate = ` +-- config.toml -- +theme = 'mytheme' +-- assets/scss/components/_foo.scss -- +/* comment line 1 */ +$foocolor: #ccc; + +foo { + color: $foocolor; +} +-- themes/mytheme/assets/scss/main.scss -- +/* comment line 1 */ +/* comment line 2 */ +@import "components/foo"; +/* comment line 4 */ + +$maincolor: #eee; + +body { + color: $maincolor; +} + +-- layouts/index.html -- +{{ $cssOpts := dict }} +{{ $r := resources.Get "scss/main.scss" | toCSS $cssOpts | minify }} +T1: {{ $r.Content }} + + ` + + c.Run("error in main", func(c *qt.C) { + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: c, + TxtarString: strings.Replace(filesTemplate, "$maincolor: #eee;", "$maincolor #eee;", 1), + NeedsOsFS: true, + }).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`themes/mytheme/assets/scss/main.scss:6:1": expected ':' after $maincolor in assignment statement`)) + fe := b.AssertIsFileError(err) + b.Assert(fe.ErrorContext(), qt.IsNotNil) + b.Assert(fe.ErrorContext().Lines, qt.DeepEquals, []string{"/* comment line 4 */", "", "$maincolor #eee;", "", "body {"}) + b.Assert(fe.ErrorContext().ChromaLexer, qt.Equals, "scss") + + }) + + c.Run("error in import", func(c *qt.C) { + b, err := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: c, + TxtarString: strings.Replace(filesTemplate, "$foocolor: #ccc;", "$foocolor #ccc;", 1), + NeedsOsFS: true, + }).BuildE() + + b.Assert(err, qt.IsNotNil) + b.Assert(err.Error(), qt.Contains, filepath.FromSlash(`assets/scss/components/_foo.scss:2:1": expected ':' after $foocolor in assignment statement`)) + fe := b.AssertIsFileError(err) + b.Assert(fe.ErrorContext(), qt.IsNotNil) + b.Assert(fe.ErrorContext().Lines, qt.DeepEquals, []string{"/* comment line 1 */", "$foocolor #ccc;", "", "foo {"}) + b.Assert(fe.ErrorContext().ChromaLexer, qt.Equals, "scss") + + }) + +} diff --git a/resources/resource_transformers/tocss/scss/tocss.go b/resources/resource_transformers/tocss/scss/tocss.go index f9f4786f5..57ac16711 100644 --- a/resources/resource_transformers/tocss/scss/tocss.go +++ b/resources/resource_transformers/tocss/scss/tocss.go @@ -20,10 +20,13 @@ import ( "fmt" "io" "path" + "path/filepath" "strings" "github.com/bep/golibsass/libsass" + "github.com/bep/golibsass/libsass/libsasserrors" + "github.com/gohugoio/hugo/common/herrors" "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugofs" "github.com/gohugoio/hugo/media" @@ -136,7 +139,14 @@ func (t *toCSSTransformation) Transform(ctx *resources.ResourceTransformationCtx res, err := t.c.toCSS(options.to, ctx.To, ctx.From) if err != nil { - return err + if sasserr, ok := err.(libsasserrors.Error); ok { + if sasserr.File == "stdin" && ctx.SourcePath != "" { + sasserr.File = t.c.sfs.RealFilename(ctx.SourcePath) + err = sasserr + } + } + return herrors.NewFileErrorFromFileInErr(err, hugofs.Os, nil) + } if options.from.EnableSourceMap && res.SourceMapContent != "" { @@ -180,7 +190,7 @@ func (c *Client) toCSS(options libsass.Options, dst io.Writer, src io.Reader) (l res, err = transpiler.Execute(in) if err != nil { - return res, fmt.Errorf("SCSS processing failed: %w", err) + return res, err } out := res.CSS |