--- /dev/null
+---
+title: TransformJS
+description: Hugo Pipes can process JS files with Babel.
+date: 2019-03-21
+publishdate: 2019-03-21
+lastmod: 2019-03-21
+categories: [asset management]
+keywords: []
+menu:
+ docs:
+ parent: "pipes"
+ weight: 75
+weight: 75
+sections_weight: 75
+draft: false
+---
+
+Any JavaScript resource file can be transpiled to another JavaScript version using `resources.TransformJS` which takes for argument the resource object and a slice of options listed below. TransformJS uses the [babel cli](https://babeljs.io/docs/en/babel-cli).
+
+
+{{% note %}}
+Hugo Pipe's TranspileJS requires the `@babel/cli` and `@babel/core` JavaScript packages to be installed in the environment (`npm install -g @babel/cli @babel/core`) along with any Babel plugin(s) or preset(s) used (e.g., `npm install -g @babel/preset-env`).
+
+If you are using the Hugo Snap package, Babel and plugin(s) need to be installed locally within your Hugo site directory, e.g., `npm install @babel/cli @babel/core` without the `-g` flag.
+{{% /note %}}
+### Options
+
+config [string]
+: Path to the Babel configuration file
+
+_If no configuration file is used:_
+
+plugins [string]
+: Comma seperated string of Babel plugins to use
+
+presets [string]
+: Comma seperated string of Babel presets to use
+
+minified [bool]
+: Save as much bytes as possible when printing
+
+noComments [bool]
+: Write comments to generated output (true by default)
+
+compact [string]
+: Do not include superfluous whitespace characters and line terminators (true/false/auto)
+
+verbose [bool]
+: Log everything
+
+### Examples
+Without a `.babelrc` file, you can simply pass the options like so:
+```go-html-template
+{{- $transpileOpts := (dict "presets" "@babel/preset-env" "minified" true "noComments" true "compact" "true" ) -}}
+{{- $transpiled := resources.Get "scripts/main.js" | transpileJS $transpileOpts -}}
+```
+
+If you rather want to use a config file, you can leave out the options in the template.
+```go-html-template
+{{- $transpiled := resources.Get "scripts/main.js" | transpileJS $transpileOpts -}}
+```
+Then, you can either create a `.babelrc` in the root of your project, or your can create a `.babel.config.js`.
+More information on these configuration files can be found here: [babel configuration](https://babeljs.io/docs/en/configuration)
+
+Finally, you can also pass a custom file path to a config file like so:
+```go-html-template
+{{- $transpileOpts := (dict "config" "config/my-babel-config.js" ) -}}
+{{- $transpiled := resources.Get "scripts/main.js" | transpileJS $transpileOpts -}}
+```
\ No newline at end of file
--- /dev/null
+// Copyright 2018 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 transpilejs
+
+import (
+ "io"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ "github.com/gohugoio/hugo/resources/internal"
+
+ "github.com/mitchellh/mapstructure"
+
+ "github.com/gohugoio/hugo/common/herrors"
+ "github.com/gohugoio/hugo/hugofs"
+ "github.com/gohugoio/hugo/resources"
+ "github.com/gohugoio/hugo/resources/resource"
+ "github.com/pkg/errors"
+)
+
+// Options from https://babeljs.io/docs/en/options
+type Options struct {
+ Config string //Custom path to config file
+ Plugins string //Comma seperated string of plugins
+ Presets string //Comma seperated string of presets
+ Minified bool //true/false
+ NoComments bool //true/false
+ Compact string //true/false/auto
+ Verbose bool //true/false
+ NoBabelrc bool //true/false
+}
+
+func DecodeOptions(m map[string]interface{}) (opts Options, err error) {
+ if m == nil {
+ return
+ }
+ err = mapstructure.WeakDecode(m, &opts)
+ return
+}
+func (opts Options) toArgs() []string {
+ var args []string
+
+ if opts.Plugins != "" {
+ args = append(args, "--plugins="+opts.Plugins)
+ }
+ if opts.Presets != "" {
+ args = append(args, "--presets="+opts.Presets)
+ }
+ if opts.Minified {
+ args = append(args, "--minified")
+ }
+ if opts.NoComments {
+ args = append(args, "--no-comments")
+ }
+ if opts.Compact != "" {
+ args = append(args, "--compact="+opts.Compact)
+ }
+ if opts.Verbose {
+ args = append(args, "--verbose")
+ }
+ if opts.NoBabelrc {
+ args = append(args, "--no-babelrc")
+ }
+ return args
+}
+
+// Client is the client used to do Babel transformations.
+type Client struct {
+ rs *resources.Spec
+}
+
+// New creates a new Client with the given specification.
+func New(rs *resources.Spec) *Client {
+ return &Client{rs: rs}
+}
+
+type babelTransformation struct {
+ options Options
+ rs *resources.Spec
+}
+
+func (t *babelTransformation) Key() internal.ResourceTransformationKey {
+ return internal.NewResourceTransformationKey("babel", t.options)
+}
+
+// Transform shells out to babel-cli to do the heavy lifting.
+// For this to work, you need some additional tools. To install them globally:
+// npm install -g @babel/core @babel/cli
+// If you want to use presets or plugins such as @babel/preset-env
+// Then you should install those globally as well. e.g:
+// npm install -g @babel/preset-env
+// Instead of installing globally, you can also install everything as a dev-dependency (--save-dev instead of -g)
+func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
+
+ const localBabelPath = "node_modules/@babel/cli/bin/"
+ const binaryName = "babel.js"
+
+ // Try first in the project's node_modules.
+ csiBinPath := filepath.Join(t.rs.WorkingDir, localBabelPath, binaryName)
+
+ binary := csiBinPath
+
+ if _, err := exec.LookPath(binary); err != nil {
+ // Try PATH
+ binary = binaryName
+ if _, err := exec.LookPath(binary); err != nil {
+
+ // This may be on a CI server etc. Will fall back to pre-built assets.
+ return herrors.ErrFeatureNotAvailable
+ }
+ }
+
+ var configFile string
+ logger := t.rs.Logger
+
+ if t.options.Config != "" {
+ configFile = t.options.Config
+ } else {
+ configFile = "babel.config.js"
+ }
+
+ configFile = filepath.Clean(configFile)
+
+ // We need an abolute filename to the config file.
+ if !filepath.IsAbs(configFile) {
+ // We resolve this against the virtual Work filesystem, to allow
+ // this config file to live in one of the themes if needed.
+ fi, err := t.rs.BaseFs.Work.Stat(configFile)
+ if err != nil {
+ if t.options.Config != "" {
+ // Only fail if the user specificed config file is not found.
+ return errors.Wrapf(err, "babel config %q not found:", configFile)
+ }
+ } else {
+ configFile = fi.(hugofs.FileMetaInfo).Meta().Filename()
+ }
+ }
+
+ var cmdArgs []string
+
+ if configFile != "" {
+ logger.INFO.Println("babel: use config file", configFile)
+ cmdArgs = []string{"--config-file", configFile}
+ }
+
+ if optArgs := t.options.toArgs(); len(optArgs) > 0 {
+ cmdArgs = append(cmdArgs, optArgs...)
+ }
+ cmdArgs = append(cmdArgs, "--filename="+ctx.SourcePath)
+
+ cmd := exec.Command(binary, cmdArgs...)
+
+ cmd.Stdout = ctx.To
+ cmd.Stderr = os.Stderr
+
+ stdin, err := cmd.StdinPipe()
+ if err != nil {
+ return err
+ }
+
+ go func() {
+ defer stdin.Close()
+ io.Copy(stdin, ctx.From)
+ }()
+
+ err = cmd.Run()
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Process transforms the given Resource with the Babel processor.
+func (c *Client) Process(res resources.ResourceTransformer, options Options) (resource.Resource, error) {
+ return res.Transform(
+ &babelTransformation{rs: c.rs, options: options},
+ )
+}
"github.com/gohugoio/hugo/resources/resource_transformers/postcss"
"github.com/gohugoio/hugo/resources/resource_transformers/templates"
"github.com/gohugoio/hugo/resources/resource_transformers/tocss/scss"
+ "github.com/gohugoio/hugo/resources/resource_transformers/transpilejs"
"github.com/spf13/cast"
)
}
return &Namespace{
- deps: deps,
- scssClient: scssClient,
- createClient: create.New(deps.ResourceSpec),
- bundlerClient: bundler.New(deps.ResourceSpec),
- integrityClient: integrity.New(deps.ResourceSpec),
- minifyClient: minifyClient,
- postcssClient: postcss.New(deps.ResourceSpec),
- templatesClient: templates.New(deps.ResourceSpec, deps),
+ deps: deps,
+ scssClient: scssClient,
+ createClient: create.New(deps.ResourceSpec),
+ bundlerClient: bundler.New(deps.ResourceSpec),
+ integrityClient: integrity.New(deps.ResourceSpec),
+ minifyClient: minifyClient,
+ postcssClient: postcss.New(deps.ResourceSpec),
+ templatesClient: templates.New(deps.ResourceSpec, deps),
+ transpileJSClient: transpilejs.New(deps.ResourceSpec),
}, nil
}
type Namespace struct {
deps *deps.Deps
- createClient *create.Client
- bundlerClient *bundler.Client
- scssClient *scss.Client
- integrityClient *integrity.Client
- minifyClient *minifier.Client
- postcssClient *postcss.Client
- templatesClient *templates.Client
+ createClient *create.Client
+ bundlerClient *bundler.Client
+ scssClient *scss.Client
+ integrityClient *integrity.Client
+ minifyClient *minifier.Client
+ postcssClient *postcss.Client
+ transpileJSClient *transpilejs.Client
+ templatesClient *templates.Client
}
// Get locates the filename given in Hugo's assets filesystem
func (ns *Namespace) PostProcess(r resource.Resource) (postpub.PostPublishedResource, error) {
return ns.deps.ResourceSpec.PostProcess(r)
+
+}
+
+// TranspileJS processes the given Resource with Babel.
+func (ns *Namespace) TranspileJS(args ...interface{}) (resource.Resource, error) {
+ r, m, err := ns.resolveArgs(args)
+ if err != nil {
+ return nil, err
+ }
+ var options transpilejs.Options
+ if m != nil {
+ options, err = transpilejs.DecodeOptions(m)
+
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ return ns.transpileJSClient.Process(r, options)
+
}
// We allow string or a map as the first argument in some cases.