diff options
author | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-07-21 17:59:03 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <bjorn.erik.pedersen@gmail.com> | 2020-07-22 10:13:30 +0200 |
commit | 0256959a358bb26b983c9d9496862b0fdf387621 (patch) | |
tree | 35578577001fb8bf330cae72195251cd73ce777d /resources/resource_transformers | |
parent | eded9ac2a05b9a7244c25c70ca8f761b69b33385 (diff) |
resources/js: Add option for setting bundle format
Fixes #7503
Diffstat (limited to 'resources/resource_transformers')
-rw-r--r-- | resources/resource_transformers/js/build.go | 126 | ||||
-rw-r--r-- | resources/resource_transformers/js/build_test.go | 34 |
2 files changed, 118 insertions, 42 deletions
diff --git a/resources/resource_transformers/js/build.go b/resources/resource_transformers/js/build.go index 488c6d1a4..8e0c7c130 100644 --- a/resources/resource_transformers/js/build.go +++ b/resources/resource_transformers/js/build.go @@ -33,8 +33,6 @@ 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 @@ -49,6 +47,11 @@ type Options struct { // Default is esnext. Target string + // The output format. + // One of: iife, cjs, esm + // Default is to esm. + Format string + // External dependencies, e.g. "react". Externals []string `hash:"set"` @@ -60,25 +63,29 @@ type Options struct { // What to use instead of React.Fragment. JSXFragment string + + mediaType media.Type + outDir string + contents string + sourcefile string + resolveDir string } -func decodeOptions(m map[string]interface{}) (opts Options, err error) { - err = mapstructure.WeakDecode(m, &opts) - if err != nil { - return +func decodeOptions(m map[string]interface{}) (Options, error) { + var opts Options + + if err := mapstructure.WeakDecode(m, &opts); err != nil { + return opts, err } if opts.TargetPath != "" { opts.TargetPath = helpers.ToSlashTrimLeading(opts.TargetPath) } - if opts.Target == "" { - opts.Target = defaultTarget - } - opts.Target = strings.ToLower(opts.Target) + opts.Format = strings.ToLower(opts.Format) - return + return opts, nil } type Client struct { @@ -114,9 +121,40 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx ctx.ReplaceOutPathExtension(".js") } + src, err := ioutil.ReadAll(ctx.From) + if err != nil { + return err + } + + sdir, sfile := path.Split(ctx.SourcePath) + opts.sourcefile = sfile + opts.resolveDir = t.sfs.RealFilename(sdir) + opts.contents = string(src) + opts.mediaType = ctx.InMediaType + + buildOptions, err := toBuildOptions(opts) + if err != nil { + return err + } + + result := api.Build(buildOptions) + if len(result.Errors) > 0 { + return fmt.Errorf("%s", result.Errors[0].Text) + } + ctx.To.Write(result.OutputFiles[0].Contents) + return nil +} + +func (c *Client) Process(res resources.ResourceTransformer, opts map[string]interface{}) (resource.Resource, error) { + return res.Transform( + &buildTransformation{rs: c.rs, sfs: c.sfs, optsm: opts}, + ) +} + +func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) { var target api.Target switch opts.Target { - case defaultTarget: + case "", "esnext": target = api.ESNext case "es5": target = api.ES5 @@ -133,11 +171,17 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx case "es2020": target = api.ES2020 default: - return fmt.Errorf("invalid target: %q", opts.Target) + err = fmt.Errorf("invalid target: %q", opts.Target) + return + } + + mediaType := opts.mediaType + if mediaType.IsZero() { + mediaType = media.JavascriptType } var loader api.Loader - switch ctx.InMediaType.SubType { + switch mediaType.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: @@ -149,29 +193,43 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx case media.JSXType.SubType: loader = api.LoaderJSX default: - return fmt.Errorf("unsupported Media Type: %q", ctx.InMediaType) - + err = fmt.Errorf("unsupported Media Type: %q", opts.mediaType) + return } - src, err := ioutil.ReadAll(ctx.From) - if err != nil { - return err + var format api.Format + // One of: iife, cjs, esm + switch opts.Format { + case "", "iife": + format = api.FormatIIFE + case "esm": + format = api.FormatESModule + case "cjs": + format = api.FormatCommonJS + default: + err = fmt.Errorf("unsupported script output format: %q", opts.Format) + return + } - sdir, sfile := path.Split(ctx.SourcePath) - sdir = t.sfs.RealFilename(sdir) + var defines map[string]string + if opts.Defines != nil { + defines = cast.ToStringMapString(opts.Defines) + } - buildOptions := api.BuildOptions{ + buildOptions = api.BuildOptions{ Outfile: "", Bundle: true, Target: target, + Format: format, MinifyWhitespace: opts.Minify, MinifyIdentifiers: opts.Minify, MinifySyntax: opts.Minify, - Defines: cast.ToStringMapString(opts.Defines), + Outdir: opts.outDir, + Defines: defines, Externals: opts.Externals, @@ -181,26 +239,12 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx //Tsconfig: opts.TSConfig, Stdin: &api.StdinOptions{ - Contents: string(src), - Sourcefile: sfile, - ResolveDir: sdir, + Contents: opts.contents, + Sourcefile: opts.sourcefile, + ResolveDir: opts.resolveDir, Loader: loader, }, } - result := api.Build(buildOptions) - if len(result.Errors) > 0 { - return fmt.Errorf("%s", result.Errors[0].Text) - } - if len(result.OutputFiles) != 1 { - return fmt.Errorf("unexpected output count: %d", len(result.OutputFiles)) - } - - ctx.To.Write(result.OutputFiles[0].Contents) - return nil -} + return -func (c *Client) Process(res resources.ResourceTransformer, opts map[string]interface{}) (resource.Resource, error) { - return res.Transform( - &buildTransformation{rs: c.rs, sfs: c.sfs, optsm: opts}, - ) } diff --git a/resources/resource_transformers/js/build_test.go b/resources/resource_transformers/js/build_test.go index 2e4c174f7..ee97dede5 100644 --- a/resources/resource_transformers/js/build_test.go +++ b/resources/resource_transformers/js/build_test.go @@ -16,6 +16,10 @@ package js import ( "testing" + "github.com/gohugoio/hugo/media" + + "github.com/evanw/esbuild/pkg/api" + qt "github.com/frankban/quicktest" ) @@ -26,9 +30,37 @@ func TestOptionKey(t *testing.T) { opts := map[string]interface{}{ "TargetPath": "foo", + "Target": "es2018", } key := (&buildTransformation{optsm: opts}).Key() - c.Assert(key.Value(), qt.Equals, "jsbuild_15565843046704064284") + c.Assert(key.Value(), qt.Equals, "jsbuild_7891849149754191852") +} + +func TestToBuildOptions(t *testing.T) { + c := qt.New(t) + + opts, err := toBuildOptions(Options{mediaType: media.JavascriptType}) + c.Assert(err, qt.IsNil) + c.Assert(opts, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ESNext, + Format: api.FormatIIFE, + Stdin: &api.StdinOptions{}, + }) + + opts, err = toBuildOptions(Options{ + Target: "es2018", Format: "cjs", Minify: true, mediaType: media.JavascriptType}) + c.Assert(err, qt.IsNil) + c.Assert(opts, qt.DeepEquals, api.BuildOptions{ + Bundle: true, + Target: api.ES2018, + Format: api.FormatCommonJS, + MinifyIdentifiers: true, + MinifySyntax: true, + MinifyWhitespace: true, + Stdin: &api.StdinOptions{}, + }) + } |