mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
pipes: Add external source map support to js.Build and Babel
Fixes #8132
This commit is contained in:
parent
0004a733c8
commit
2c8b5d9165
6 changed files with 138 additions and 4 deletions
|
@ -109,14 +109,16 @@ document.body.textContent = greeter(user);`
|
||||||
JS: {{ template "print" $js }}
|
JS: {{ template "print" $js }}
|
||||||
{{ $jsx := resources.Get "js/myjsx.jsx" | js.Build $options }}
|
{{ $jsx := resources.Get "js/myjsx.jsx" | js.Build $options }}
|
||||||
JSX: {{ template "print" $jsx }}
|
JSX: {{ template "print" $jsx }}
|
||||||
{{ $ts := resources.Get "js/myts.ts" | js.Build }}
|
{{ $ts := resources.Get "js/myts.ts" | js.Build (dict "sourcemap" "inline")}}
|
||||||
TS: {{ template "print" $ts }}
|
TS: {{ template "print" $ts }}
|
||||||
|
{{ $ts2 := resources.Get "js/myts.ts" | js.Build (dict "sourcemap" "external" "TargetPath" "js/myts2.js")}}
|
||||||
|
TS2: {{ template "print" $ts2 }}
|
||||||
{{ define "print" }}RelPermalink: {{.RelPermalink}}|MIME: {{ .MediaType }}|Content: {{ .Content | safeJS }}{{ end }}
|
{{ define "print" }}RelPermalink: {{.RelPermalink}}|MIME: {{ .MediaType }}|Content: {{ .Content | safeJS }}{{ end }}
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
jsDir := filepath.Join(workDir, "assets", "js")
|
jsDir := filepath.Join(workDir, "assets", "js")
|
||||||
|
fmt.Println(workDir)
|
||||||
b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
|
b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
|
||||||
b.Assert(os.Chdir(workDir), qt.IsNil)
|
b.Assert(os.Chdir(workDir), qt.IsNil)
|
||||||
b.WithSourceFile("package.json", packageJSON)
|
b.WithSourceFile("package.json", packageJSON)
|
||||||
|
@ -133,6 +135,8 @@ TS: {{ template "print" $ts }}
|
||||||
|
|
||||||
b.Build(BuildCfg{})
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
|
b.AssertFileContent("public/js/myts.js", `//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJz`)
|
||||||
|
b.AssertFileContent("public/js/myts2.js.map", `"version": 3,`)
|
||||||
b.AssertFileContent("public/index.html", `
|
b.AssertFileContent("public/index.html", `
|
||||||
console.log("included");
|
console.log("included");
|
||||||
if (hasSpace.test(string))
|
if (hasSpace.test(string))
|
||||||
|
|
|
@ -78,6 +78,15 @@ class Car {
|
||||||
this.carname = brand;
|
this.carname = brand;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
js2 := `
|
||||||
|
/* A Car2 */
|
||||||
|
class Car2 {
|
||||||
|
constructor(brand) {
|
||||||
|
this.carname = brand;
|
||||||
|
}
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-babel")
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-babel")
|
||||||
|
@ -103,11 +112,18 @@ class Car {
|
||||||
{{ $transpiled := resources.Get "js/main.js" | babel -}}
|
{{ $transpiled := resources.Get "js/main.js" | babel -}}
|
||||||
Transpiled: {{ $transpiled.Content | safeJS }}
|
Transpiled: {{ $transpiled.Content | safeJS }}
|
||||||
|
|
||||||
|
{{ $transpiled := resources.Get "js/main2.js" | babel (dict "sourceMap" "inline") -}}
|
||||||
|
Transpiled2: {{ $transpiled.Content | safeJS }}
|
||||||
|
|
||||||
|
{{ $transpiled := resources.Get "js/main2.js" | babel (dict "sourceMap" "external") -}}
|
||||||
|
Transpiled3: {{ $transpiled.Permalink }}
|
||||||
|
|
||||||
`)
|
`)
|
||||||
|
|
||||||
jsDir := filepath.Join(workDir, "assets", "js")
|
jsDir := filepath.Join(workDir, "assets", "js")
|
||||||
b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
|
b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
|
||||||
b.WithSourceFile("assets/js/main.js", js)
|
b.WithSourceFile("assets/js/main.js", js)
|
||||||
|
b.WithSourceFile("assets/js/main2.js", js2)
|
||||||
b.WithSourceFile("package.json", packageJSON)
|
b.WithSourceFile("package.json", packageJSON)
|
||||||
b.WithSourceFile("babel.config.js", babelConfig)
|
b.WithSourceFile("babel.config.js", babelConfig)
|
||||||
|
|
||||||
|
@ -129,4 +145,21 @@ var Car = function Car(brand) {
|
||||||
this.carname = brand;
|
this.carname = brand;
|
||||||
};
|
};
|
||||||
`)
|
`)
|
||||||
|
b.AssertFileContent("public/index.html", `
|
||||||
|
var Car2 = function Car2(brand) {
|
||||||
|
_classCallCheck(this, Car2);
|
||||||
|
|
||||||
|
this.carname = brand;
|
||||||
|
};
|
||||||
|
`)
|
||||||
|
b.AssertFileContent("public/js/main2.js", `
|
||||||
|
var Car2 = function Car2(brand) {
|
||||||
|
_classCallCheck(this, Car2);
|
||||||
|
|
||||||
|
this.carname = brand;
|
||||||
|
};
|
||||||
|
`)
|
||||||
|
b.AssertFileContent("public/js/main2.js.map", `{"version":3,`)
|
||||||
|
b.AssertFileContent("public/index.html", `
|
||||||
|
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozL`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,11 @@ package babel
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/cli/safeexec"
|
"github.com/cli/safeexec"
|
||||||
|
@ -43,8 +47,10 @@ type Options struct {
|
||||||
Compact *bool
|
Compact *bool
|
||||||
Verbose bool
|
Verbose bool
|
||||||
NoBabelrc bool
|
NoBabelrc bool
|
||||||
|
SourceMap string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DecodeOptions decodes options to and generates command flags
|
||||||
func DecodeOptions(m map[string]interface{}) (opts Options, err error) {
|
func DecodeOptions(m map[string]interface{}) (opts Options, err error) {
|
||||||
if m == nil {
|
if m == nil {
|
||||||
return
|
return
|
||||||
|
@ -56,6 +62,14 @@ func DecodeOptions(m map[string]interface{}) (opts Options, err error) {
|
||||||
func (opts Options) toArgs() []string {
|
func (opts Options) toArgs() []string {
|
||||||
var args []string
|
var args []string
|
||||||
|
|
||||||
|
// external is not a known constant on the babel command line
|
||||||
|
// .sourceMaps must be a boolean, "inline", "both", or undefined
|
||||||
|
switch opts.SourceMap {
|
||||||
|
case "external":
|
||||||
|
args = append(args, "--source-maps")
|
||||||
|
case "inline":
|
||||||
|
args = append(args, "--source-maps=inline")
|
||||||
|
}
|
||||||
if opts.Minified {
|
if opts.Minified {
|
||||||
args = append(args, "--minified")
|
args = append(args, "--minified")
|
||||||
}
|
}
|
||||||
|
@ -141,6 +155,8 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx.ReplaceOutPathExtension(".js")
|
||||||
|
|
||||||
var cmdArgs []string
|
var cmdArgs []string
|
||||||
|
|
||||||
if configFile != "" {
|
if configFile != "" {
|
||||||
|
@ -153,13 +169,24 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
}
|
}
|
||||||
cmdArgs = append(cmdArgs, "--filename="+ctx.SourcePath)
|
cmdArgs = append(cmdArgs, "--filename="+ctx.SourcePath)
|
||||||
|
|
||||||
|
// Create compile into a real temp file:
|
||||||
|
// 1. separate stdout/stderr messages from babel (https://github.com/gohugoio/hugo/issues/8136)
|
||||||
|
// 2. allow generation and retrieval of external source map.
|
||||||
|
compileOutput, err := ioutil.TempFile("", "compileOut-*.js")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cmdArgs = append(cmdArgs, "--out-file="+compileOutput.Name())
|
||||||
|
defer os.Remove(compileOutput.Name())
|
||||||
|
|
||||||
cmd, err := hexec.SafeCommand(binary, cmdArgs...)
|
cmd, err := hexec.SafeCommand(binary, cmdArgs...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd.Stdout = ctx.To
|
|
||||||
cmd.Stderr = io.MultiWriter(infoW, &errBuf)
|
cmd.Stderr = io.MultiWriter(infoW, &errBuf)
|
||||||
|
cmd.Stdout = cmd.Stderr
|
||||||
cmd.Env = hugo.GetExecEnviron(t.rs.WorkingDir, t.rs.Cfg, t.rs.BaseFs.Assets.Fs)
|
cmd.Env = hugo.GetExecEnviron(t.rs.WorkingDir, t.rs.Cfg, t.rs.BaseFs.Assets.Fs)
|
||||||
|
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
|
@ -177,6 +204,28 @@ func (t *babelTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
return errors.Wrap(err, errBuf.String())
|
return errors.Wrap(err, errBuf.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
content, err := ioutil.ReadAll(compileOutput)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
mapFile := compileOutput.Name() + ".map"
|
||||||
|
if _, err := os.Stat(mapFile); err == nil {
|
||||||
|
defer os.Remove(mapFile)
|
||||||
|
sourceMap, err := ioutil.ReadFile(mapFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = ctx.PublishSourceMap(string(sourceMap)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
targetPath := path.Base(ctx.OutPath) + ".map"
|
||||||
|
re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`)
|
||||||
|
content = []byte(re.ReplaceAllString(string(content), "//# sourceMappingURL="+targetPath+"\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.To.Write(content)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path"
|
||||||
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
|
@ -92,6 +94,14 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if buildOptions.Sourcemap == api.SourceMapExternal && buildOptions.Outdir == "" {
|
||||||
|
buildOptions.Outdir, err = ioutil.TempDir(os.TempDir(), "compileOutput")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer os.Remove(buildOptions.Outdir)
|
||||||
|
}
|
||||||
|
|
||||||
result := api.Build(buildOptions)
|
result := api.Build(buildOptions)
|
||||||
|
|
||||||
if len(result.Errors) > 0 {
|
if len(result.Errors) > 0 {
|
||||||
|
@ -145,7 +155,25 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
return errors[0]
|
return errors[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx.To.Write(result.OutputFiles[0].Contents)
|
if buildOptions.Sourcemap == api.SourceMapExternal {
|
||||||
|
content := string(result.OutputFiles[1].Contents)
|
||||||
|
symPath := path.Base(ctx.OutPath) + ".map"
|
||||||
|
re := regexp.MustCompile(`//# sourceMappingURL=.*\n?`)
|
||||||
|
content = re.ReplaceAllString(content, "//# sourceMappingURL="+symPath+"\n")
|
||||||
|
|
||||||
|
if err = ctx.PublishSourceMap(string(result.OutputFiles[0].Contents)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err := ctx.To.Write([]byte(content))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_, err := ctx.To.Write(result.OutputFiles[0].Contents)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -338,6 +338,8 @@ func toBuildOptions(opts Options) (buildOptions api.BuildOptions, err error) {
|
||||||
switch opts.SourceMap {
|
switch opts.SourceMap {
|
||||||
case "inline":
|
case "inline":
|
||||||
sourceMap = api.SourceMapInline
|
sourceMap = api.SourceMapInline
|
||||||
|
case "external":
|
||||||
|
sourceMap = api.SourceMapExternal
|
||||||
case "":
|
case "":
|
||||||
sourceMap = api.SourceMapNone
|
sourceMap = api.SourceMapNone
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -109,4 +109,22 @@ func TestToBuildOptions(t *testing.T) {
|
||||||
Loader: api.LoaderJS,
|
Loader: api.LoaderJS,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
opts, err = toBuildOptions(Options{
|
||||||
|
Target: "es2018", Format: "cjs", Minify: true, mediaType: media.JavascriptType,
|
||||||
|
SourceMap: "external",
|
||||||
|
})
|
||||||
|
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,
|
||||||
|
Sourcemap: api.SourceMapExternal,
|
||||||
|
Stdin: &api.StdinOptions{
|
||||||
|
Loader: api.LoaderJS,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue