diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-07-12 12:47:14 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-07-13 10:56:23 +0200 |
commit | 9df98ec49ca9fa326125ccfee626b6e46c6ab14b (patch) | |
tree | cdfcfcc9522366d69b7b543638b689852a5e1252 /resources/resource_transformers | |
parent | 2fc33807077cd25bf91f2298bf1a8ace126881a7 (diff) |
Add proper Media Type handling in js.Build
See #732
Diffstat (limited to 'resources/resource_transformers')
-rw-r--r-- | resources/resource_transformers/js/build.go | 109 | ||||
-rw-r--r-- | resources/resource_transformers/js/build_test.go | 69 |
2 files changed, 151 insertions, 27 deletions
diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go index 6224ee178..c48778692 100644 --- a/resources/resource_transformers/js/build.go +++ b/resources/resource_transformers/js/build.go @@ -17,8 +17,11 @@ import ( "fmt" "io/ioutil" "path" + "strings" + "github.com/gohugoio/hugo/helpers" "github.com/gohugoio/hugo/hugolib/filesystems" + "github.com/gohugoio/hugo/media" "github.com/gohugoio/hugo/resources/internal" "github.com/mitchellh/mapstructure" @@ -28,15 +31,46 @@ import ( "github.com/gohugoio/hugo/resources/resource" ) +const defaultTarget = "esnext" + type Options struct { + // If not set, the source path will be used as the base target path. + // Note that the target path's extension may change if the target MIME type + // is different, e.g. when the source is TypeScript. + TargetPath string + + // Whether to minify to output. + Minify bool + + // The language target. + // One of: es2015, es2016, es2017, es2018, es2019, es2020 or esnext. + // Default is esnext. + Target string + + // External dependencies, e.g. "react". + Externals []string `hash:"set"` + + // What to use instead of React.createElement. + JSXFactory string + + // What to use instead of React.Fragment. + JSXFragment string +} + +type internalOptions struct { + TargetPath string Minify bool - Externals []string Target string - Loader string - Defines map[string]string JSXFactory string JSXFragment string - TSConfig string + + Externals []string `hash:"set"` + + // These are currently not exposed in the public Options struct, + // but added here to make the options hash as stable as possible for + // whenever we do. + Defines map[string]string + TSConfig string } func DecodeOptions(m map[string]interface{}) (opts Options, err error) { @@ -44,6 +78,13 @@ func DecodeOptions(m map[string]interface{}) (opts Options, err error) { return } err = mapstructure.WeakDecode(m, &opts) + + if opts.TargetPath != "" { + opts.TargetPath = helpers.ToSlashTrimLeading(opts.TargetPath) + } + + opts.Target = strings.ToLower(opts.Target) + return } @@ -57,7 +98,7 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) *Client { } type buildTransformation struct { - options Options + options internalOptions rs *resources.Spec sfs *filesystems.SourceFilesystem } @@ -67,9 +108,17 @@ func (t *buildTransformation) Key() internal.ResourceTransformationKey { } func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error { + ctx.OutMediaType = media.JavascriptType + + if t.options.TargetPath != "" { + ctx.OutPath = t.options.TargetPath + } else { + ctx.ReplaceOutPathExtension(".js") + } + var target api.Target switch t.options.Target { - case "", "esnext": + case defaultTarget: target = api.ESNext case "es6", "es2015": target = api.ES2015 @@ -88,29 +137,20 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx } var loader api.Loader - switch t.options.Loader { - case "", "js": + switch ctx.InMediaType.SubType { + // TODO(bep) ESBuild support a set of other loaders, but I currently fail + // to see the relevance. That may change as we start using this. + case media.JavascriptType.SubType: loader = api.LoaderJS - case "jsx": - loader = api.LoaderJSX - case "ts": + case media.TypeScriptType.SubType: loader = api.LoaderTS - case "tsx": + case media.TSXType.SubType: loader = api.LoaderTSX - case "json": - loader = api.LoaderJSON - case "text": - loader = api.LoaderText - case "base64": - loader = api.LoaderBase64 - case "dataURL": - loader = api.LoaderDataURL - case "file": - loader = api.LoaderFile - case "binary": - loader = api.LoaderBinary + case media.JSXType.SubType: + loader = api.LoaderJSX default: - return fmt.Errorf("invalid loader: %q", t.options.Loader) + return fmt.Errorf("unsupported Media Type: %q", ctx.InMediaType) + } src, err := ioutil.ReadAll(ctx.From) @@ -159,8 +199,23 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx return nil } -func (c *Client) Process(res resources.ResourceTransformer, options Options) (resource.Resource, error) { +func (c *Client) Process(res resources.ResourceTransformer, opts Options) (resource.Resource, error) { return res.Transform( - &buildTransformation{rs: c.rs, sfs: c.sfs, options: options}, + &buildTransformation{rs: c.rs, sfs: c.sfs, options: toInternalOptions(opts)}, ) } + +func toInternalOptions(opts Options) internalOptions { + target := opts.Target + if target == "" { + target = defaultTarget + } + return internalOptions{ + TargetPath: opts.TargetPath, + Minify: opts.Minify, + Target: target, + Externals: opts.Externals, + JSXFactory: opts.JSXFactory, + JSXFragment: opts.JSXFragment, + } +} diff --git a/resources/resource_transformers/js/build_test.go b/resources/resource_transformers/js/build_test.go new file mode 100644 index 000000000..3f2a1e104 --- /dev/null +++ b/resources/resource_transformers/js/build_test.go @@ -0,0 +1,69 @@ +// Copyright 2020 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package js + +import ( + "testing" + + qt "github.com/frankban/quicktest" +) + +// This test is added to test/warn against breaking the "stability" of the +// cache key. It's sometimes needed to break this, but should be avoided if possible. +func TestOptionKey(t *testing.T) { + c := qt.New(t) + + opts := internalOptions{ + TargetPath: "foo", + } + + key := (&buildTransformation{options: opts}).Key() + + c.Assert(key.Value(), qt.Equals, "jsbuild_9405671309963492201") +} + +func TestToInternalOptions(t *testing.T) { + c := qt.New(t) + + o := Options{ + TargetPath: "v1", + Target: "v2", + JSXFactory: "v3", + JSXFragment: "v4", + Externals: []string{"react"}, + Minify: true, + } + + c.Assert(toInternalOptions(o), qt.DeepEquals, internalOptions{ + TargetPath: "v1", + Minify: true, + Target: "v2", + JSXFactory: "v3", + JSXFragment: "v4", + Externals: []string{"react"}, + Defines: nil, + TSConfig: "", + }) + + c.Assert(toInternalOptions(Options{}), qt.DeepEquals, internalOptions{ + TargetPath: "", + Minify: false, + Target: "esnext", + JSXFactory: "", + JSXFragment: "", + Externals: nil, + Defines: nil, + TSConfig: "", + }) +} |