summaryrefslogtreecommitdiffstats
path: root/resources/resource_transformers/postcss
diff options
context:
space:
mode:
authorBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-05-14 15:51:04 +0200
committerBjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com>2022-05-15 20:25:25 +0200
commit4b189d8fd93d3fa326b31d451d5594c917e6c714 (patch)
treec3515876c474caab2df6d0a34291bcc3ca8e75df /resources/resource_transformers/postcss
parentc2fa0a33209da7cfd9a09f8fc528415578a9edf8 (diff)
postcss: Fix import error handling
Note that we will now fail if `inlineImports` is enabled and we cannot resolve an import. You can work around this by either: * Use url imports or imports with media queries. * Set `skipInlineImportsNotFound=true` in the options Also get the argument order in the different NewFileError* funcs in line. Fixes #9895
Diffstat (limited to 'resources/resource_transformers/postcss')
-rw-r--r--resources/resource_transformers/postcss/integration_test.go48
-rw-r--r--resources/resource_transformers/postcss/postcss.go42
-rw-r--r--resources/resource_transformers/postcss/postcss_test.go9
3 files changed, 84 insertions, 15 deletions
diff --git a/resources/resource_transformers/postcss/integration_test.go b/resources/resource_transformers/postcss/integration_test.go
index 4101818be..69f0964d0 100644
--- a/resources/resource_transformers/postcss/integration_test.go
+++ b/resources/resource_transformers/postcss/integration_test.go
@@ -48,7 +48,7 @@ class-in-b {
@tailwind base;
@tailwind components;
@tailwind utilities;
-@import "components/all.css";
+ @import "components/all.css";
h1 {
@apply text-2xl font-bold;
}
@@ -140,3 +140,49 @@ func TestTransformPostCSSError(t *testing.T) {
c.Assert(err.Error(), qt.Contains, "a.css:4:2")
}
+
+// #9895
+func TestTransformPostCSSImportError(t *testing.T) {
+ if !htesting.IsCI() {
+ t.Skip("Skip long running test when running locally")
+ }
+
+ c := qt.New(t)
+
+ s, err := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: c,
+ NeedsOsFS: true,
+ NeedsNpmInstall: true,
+ LogLevel: jww.LevelInfo,
+ TxtarString: strings.ReplaceAll(postCSSIntegrationTestFiles, `@import "components/all.css";`, `@import "components/doesnotexist.css";`),
+ }).BuildE()
+
+ s.AssertIsFileError(err)
+ c.Assert(err.Error(), qt.Contains, "styles.css:4:3")
+ c.Assert(err.Error(), qt.Contains, filepath.FromSlash(`failed to resolve CSS @import "css/components/doesnotexist.css"`))
+
+}
+
+func TestTransformPostCSSImporSkipInlineImportsNotFound(t *testing.T) {
+ if !htesting.IsCI() {
+ t.Skip("Skip long running test when running locally")
+ }
+
+ c := qt.New(t)
+
+ files := strings.ReplaceAll(postCSSIntegrationTestFiles, `@import "components/all.css";`, `@import "components/doesnotexist.css";`)
+ files = strings.ReplaceAll(files, `{{ $options := dict "inlineImports" true }}`, `{{ $options := dict "inlineImports" true "skipInlineImportsNotFound" true }}`)
+
+ s := hugolib.NewIntegrationTestBuilder(
+ hugolib.IntegrationTestConfig{
+ T: c,
+ NeedsOsFS: true,
+ NeedsNpmInstall: true,
+ LogLevel: jww.LevelInfo,
+ TxtarString: files,
+ }).Build()
+
+ s.AssertFileContent("public/css/styles.css", filepath.FromSlash(`@import "components/doesnotexist.css";`))
+
+}
diff --git a/resources/resource_transformers/postcss/postcss.go b/resources/resource_transformers/postcss/postcss.go
index a5c86df6f..325dc1ec2 100644
--- a/resources/resource_transformers/postcss/postcss.go
+++ b/resources/resource_transformers/postcss/postcss.go
@@ -28,6 +28,7 @@ import (
"github.com/gohugoio/hugo/common/collections"
"github.com/gohugoio/hugo/common/hexec"
+ "github.com/gohugoio/hugo/common/text"
"github.com/gohugoio/hugo/hugofs"
"github.com/gohugoio/hugo/common/hugo"
@@ -49,9 +50,10 @@ import (
const importIdentifier = "@import"
-var cssSyntaxErrorRe = regexp.MustCompile(`> (\d+) \|`)
-
-var shouldImportRe = regexp.MustCompile(`^@import ["'].*["'];?\s*(/\*.*\*/)?$`)
+var (
+ cssSyntaxErrorRe = regexp.MustCompile(`> (\d+) \|`)
+ shouldImportRe = regexp.MustCompile(`^@import ["'].*["'];?\s*(/\*.*\*/)?$`)
+)
// New creates a new Client with the given specification.
func New(rs *resources.Spec) *Client {
@@ -100,6 +102,12 @@ type Options struct {
// so you can have @import anywhere in the file.
InlineImports bool
+ // When InlineImports is enabled, we fail the build if an import cannot be resolved.
+ // You can enable this to allow the build to continue and leave the import statement in place.
+ // Note that the inline importer does not process url location or imports with media queries,
+ // so those will be left as-is even without enabling this option.
+ SkipInlineImportsNotFound bool
+
// Options for when not using a config file
Use string // List of postcss plugins to use
Parser string // Custom postcss parser
@@ -204,6 +212,7 @@ func (t *postcssTransformation) Transform(ctx *resources.ResourceTransformationC
imp := newImportResolver(
ctx.From,
ctx.InPath,
+ t.options,
t.rs.Assets.Fs, t.rs.Logger,
)
@@ -239,6 +248,7 @@ type fileOffset struct {
type importResolver struct {
r io.Reader
inPath string
+ opts Options
contentSeen map[string]bool
linemap map[int]fileOffset
@@ -246,12 +256,13 @@ type importResolver struct {
logger loggers.Logger
}
-func newImportResolver(r io.Reader, inPath string, fs afero.Fs, logger loggers.Logger) *importResolver {
+func newImportResolver(r io.Reader, inPath string, opts Options, fs afero.Fs, logger loggers.Logger) *importResolver {
return &importResolver{
r: r,
inPath: inPath,
fs: fs, logger: logger,
linemap: make(map[int]fileOffset), contentSeen: make(map[string]bool),
+ opts: opts,
}
}
@@ -282,21 +293,32 @@ func (imp *importResolver) importRecursive(
i := 0
for offset, line := range lines {
i++
- line = strings.TrimSpace(line)
+ lineTrimmed := strings.TrimSpace(line)
+ column := strings.Index(line, lineTrimmed)
+ line = lineTrimmed
if !imp.shouldImport(line) {
trackLine(i, offset, line)
} else {
- i--
path := strings.Trim(strings.TrimPrefix(line, importIdentifier), " \"';")
filename := filepath.Join(basePath, path)
importContent, hash := imp.contentHash(filename)
+
if importContent == nil {
- trackLine(i, offset, "ERROR")
- imp.logger.Warnf("postcss: Failed to resolve CSS @import in %q for path %q", inPath, filename)
- continue
+ if imp.opts.SkipInlineImportsNotFound {
+ trackLine(i, offset, line)
+ continue
+ }
+ pos := text.Position{
+ Filename: inPath,
+ LineNumber: offset + 1,
+ ColumnNumber: column + 1,
+ }
+ return 0, "", herrors.NewFileErrorFromFileInPos(fmt.Errorf("failed to resolve CSS @import %q", filename), pos, imp.fs, nil)
}
+ i--
+
if imp.contentSeen[hash] {
i++
// Just replace the line with an empty string.
@@ -399,7 +421,7 @@ func (imp *importResolver) toFileError(output string) error {
}
defer f.Close()
- ferr := herrors.NewFileError(realFilename, inErr)
+ ferr := herrors.NewFileError(inErr, realFilename)
pos := ferr.Position()
pos.LineNumber = file.Offset + 1
return ferr.UpdatePosition(pos).UpdateContent(f, nil)
diff --git a/resources/resource_transformers/postcss/postcss_test.go b/resources/resource_transformers/postcss/postcss_test.go
index 4548bca98..f5d49300a 100644
--- a/resources/resource_transformers/postcss/postcss_test.go
+++ b/resources/resource_transformers/postcss/postcss_test.go
@@ -60,6 +60,7 @@ func TestShouldImport(t *testing.T) {
{input: `@import 'navigation.css';`, expect: true},
{input: `@import url("navigation.css");`, expect: false},
{input: `@import url('https://fonts.googleapis.com/css?family=Open+Sans:400,400i,800,800i&display=swap');`, expect: false},
+ {input: `@import "printstyle.css" print;`, expect: false},
} {
c.Assert(imp.shouldImport(test.input), qt.Equals, test.expect)
}
@@ -88,12 +89,12 @@ A_STYLE2
@import "b.css";
LOCAL_STYLE
@import "c.css";
-@import "e.css";
-@import "missing.css";`)
+@import "e.css";`)
imp := newImportResolver(
mainStyles,
"styles.css",
+ Options{},
fs, loggers.NewErrorLogger(),
)
@@ -108,8 +109,7 @@ C_STYLE
A_STYLE1
A_STYLE2
LOCAL_STYLE
-E_STYLE
-@import "missing.css";`)
+E_STYLE`)
dline := imp.linemap[3]
c.Assert(dline, qt.DeepEquals, fileOffset{
@@ -151,6 +151,7 @@ LOCAL_STYLE
imp := newImportResolver(
strings.NewReader(mainStyles),
"styles.css",
+ Options{},
fs, logger,
)