mirror of
https://github.com/gohugoio/hugo.git
synced 2024-11-21 20:46:30 -05:00
parent
2fc3380707
commit
9df98ec49c
10 changed files with 400 additions and 145 deletions
2
go.mod
2
go.mod
|
@ -15,7 +15,7 @@ require (
|
||||||
github.com/bep/tmc v0.5.1
|
github.com/bep/tmc v0.5.1
|
||||||
github.com/disintegration/gift v1.2.1
|
github.com/disintegration/gift v1.2.1
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/evanw/esbuild v0.6.1
|
github.com/evanw/esbuild v0.6.2
|
||||||
github.com/fortytw2/leaktest v1.3.0
|
github.com/fortytw2/leaktest v1.3.0
|
||||||
github.com/frankban/quicktest v1.7.2
|
github.com/frankban/quicktest v1.7.2
|
||||||
github.com/fsnotify/fsnotify v1.4.7
|
github.com/fsnotify/fsnotify v1.4.7
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -119,6 +119,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/evanw/esbuild v0.6.1 h1:XkoACQJCiqUmwySWssu0/iUj7J6IbNMR9dqbSbh1/vk=
|
github.com/evanw/esbuild v0.6.1 h1:XkoACQJCiqUmwySWssu0/iUj7J6IbNMR9dqbSbh1/vk=
|
||||||
github.com/evanw/esbuild v0.6.1/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
|
github.com/evanw/esbuild v0.6.1/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
|
||||||
|
github.com/evanw/esbuild v0.6.2 h1:pp33TIPgiHCtKL/gMW/V/PFHWNx/5cDTqbJHqAiy0jg=
|
||||||
|
github.com/evanw/esbuild v0.6.2/go.mod h1:mptxmSXIzBIKKCe4jo9A5SToEd1G+AKZ9JmY85dYRJ0=
|
||||||
github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q=
|
github.com/fortytw2/leaktest v1.2.0 h1:cj6GCiwJDH7l3tMHLjZDo0QqPtrXJiWSI9JgpeQKw+Q=
|
||||||
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
github.com/fortytw2/leaktest v1.2.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||||
|
|
|
@ -15,7 +15,9 @@ package hugolib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/htesting"
|
"github.com/gohugoio/hugo/htesting"
|
||||||
|
@ -29,11 +31,15 @@ import (
|
||||||
"github.com/gohugoio/hugo/common/loggers"
|
"github.com/gohugoio/hugo/common/loggers"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestJS_Build(t *testing.T) {
|
func TestJSBuildWithNPM(t *testing.T) {
|
||||||
if !isCI() {
|
if !isCI() {
|
||||||
t.Skip("skip (relative) long running modules test when running locally")
|
t.Skip("skip (relative) long running modules test when running locally")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if runtime.GOOS == "windows" {
|
||||||
|
t.Skip("skip NPM test on Windows")
|
||||||
|
}
|
||||||
|
|
||||||
wd, _ := os.Getwd()
|
wd, _ := os.Getwd()
|
||||||
defer func() {
|
defer func() {
|
||||||
os.Chdir(wd)
|
os.Chdir(wd)
|
||||||
|
@ -43,13 +49,44 @@ func TestJS_Build(t *testing.T) {
|
||||||
|
|
||||||
mainJS := `
|
mainJS := `
|
||||||
import "./included";
|
import "./included";
|
||||||
|
import { toCamelCase } from "to-camel-case";
|
||||||
|
|
||||||
console.log("main");
|
console.log("main");
|
||||||
`
|
console.log("To camel:", toCamelCase("space case"));
|
||||||
|
`
|
||||||
includedJS := `
|
includedJS := `
|
||||||
console.log("included");
|
console.log("included");
|
||||||
|
|
||||||
`
|
`
|
||||||
|
|
||||||
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-babel")
|
jsxContent := `
|
||||||
|
import * as React from 'react'
|
||||||
|
import * as ReactDOM from 'react-dom'
|
||||||
|
|
||||||
|
ReactDOM.render(
|
||||||
|
<h1>Hello, world!</h1>,
|
||||||
|
document.getElementById('root')
|
||||||
|
);
|
||||||
|
`
|
||||||
|
|
||||||
|
tsContent := `function greeter(person: string) {
|
||||||
|
return "Hello, " + person;
|
||||||
|
}
|
||||||
|
|
||||||
|
let user = [0, 1, 2];
|
||||||
|
|
||||||
|
document.body.textContent = greeter(user);`
|
||||||
|
|
||||||
|
packageJSON := `{
|
||||||
|
"scripts": {},
|
||||||
|
|
||||||
|
"dependencies": {
|
||||||
|
"to-camel-case": "1.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-js-npm")
|
||||||
c.Assert(err, qt.IsNil)
|
c.Assert(err, qt.IsNil)
|
||||||
defer clean()
|
defer clean()
|
||||||
|
|
||||||
|
@ -65,23 +102,99 @@ func TestJS_Build(t *testing.T) {
|
||||||
b.WithContent("p1.md", "")
|
b.WithContent("p1.md", "")
|
||||||
|
|
||||||
b.WithTemplates("index.html", `
|
b.WithTemplates("index.html", `
|
||||||
{{ $options := dict "minify" true }}
|
{{ $options := dict "minify" false "externals" (slice "react" "react-dom") }}
|
||||||
{{ $transpiled := resources.Get "js/main.js" | js.Build $options }}
|
{{ $js := resources.Get "js/main.js" | js.Build $options }}
|
||||||
Built: {{ $transpiled.Content | safeJS }}
|
JS: {{ template "print" $js }}
|
||||||
`)
|
{{ $jsx := resources.Get "js/myjsx.jsx" | js.Build $options }}
|
||||||
|
JSX: {{ template "print" $jsx }}
|
||||||
|
{{ $ts := resources.Get "js/myts.ts" | js.Build }}
|
||||||
|
TS: {{ template "print" $ts }}
|
||||||
|
|
||||||
|
{{ define "print" }}RelPermalink: {{.RelPermalink}}|MIME: {{ .MediaType }}|Content: {{ .Content | safeJS }}{{ end }}
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
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.Assert(os.Chdir(workDir), qt.IsNil)
|
||||||
|
b.WithSourceFile("package.json", packageJSON)
|
||||||
|
b.WithSourceFile("assets/js/main.js", mainJS)
|
||||||
|
b.WithSourceFile("assets/js/myjsx.jsx", jsxContent)
|
||||||
|
b.WithSourceFile("assets/js/myts.ts", tsContent)
|
||||||
|
|
||||||
|
b.WithSourceFile("assets/js/included.js", includedJS)
|
||||||
|
|
||||||
|
out, err := exec.Command("npm", "install").CombinedOutput()
|
||||||
|
b.Assert(err, qt.IsNil, qt.Commentf(string(out)))
|
||||||
|
|
||||||
|
b.Build(BuildCfg{})
|
||||||
|
|
||||||
|
b.AssertFileContent("public/index.html", `
|
||||||
|
console.log("included");
|
||||||
|
if (hasSpace.test(string))
|
||||||
|
const React = __toModule(require("react"));
|
||||||
|
function greeter(person) {
|
||||||
|
`)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJSBuild(t *testing.T) {
|
||||||
|
if !isCI() {
|
||||||
|
t.Skip("skip (relative) long running modules test when running locally")
|
||||||
|
}
|
||||||
|
|
||||||
|
wd, _ := os.Getwd()
|
||||||
|
defer func() {
|
||||||
|
os.Chdir(wd)
|
||||||
|
}()
|
||||||
|
|
||||||
|
c := qt.New(t)
|
||||||
|
|
||||||
|
mainJS := `
|
||||||
|
import "./included";
|
||||||
|
|
||||||
|
console.log("main");
|
||||||
|
|
||||||
|
`
|
||||||
|
includedJS := `
|
||||||
|
console.log("included");
|
||||||
|
|
||||||
|
`
|
||||||
|
|
||||||
|
workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-js")
|
||||||
|
c.Assert(err, qt.IsNil)
|
||||||
|
defer clean()
|
||||||
|
|
||||||
|
v := viper.New()
|
||||||
|
v.Set("workingDir", workDir)
|
||||||
|
v.Set("disableKinds", []string{"taxonomy", "term", "page"})
|
||||||
|
b := newTestSitesBuilder(t).WithLogger(loggers.NewWarningLogger())
|
||||||
|
|
||||||
|
b.Fs = hugofs.NewDefault(v)
|
||||||
|
b.WithWorkingDir(workDir)
|
||||||
|
b.WithViper(v)
|
||||||
|
b.WithContent("p1.md", "")
|
||||||
|
|
||||||
|
b.WithTemplates("index.html", `
|
||||||
|
{{ $js := resources.Get "js/main.js" | js.Build }}
|
||||||
|
JS: {{ template "print" $js }}
|
||||||
|
|
||||||
|
|
||||||
|
{{ define "print" }}RelPermalink: {{.RelPermalink}}|MIME: {{ .MediaType }}|Content: {{ .Content | safeJS }}{{ end }}
|
||||||
|
|
||||||
|
`)
|
||||||
|
|
||||||
|
jsDir := filepath.Join(workDir, "assets", "js")
|
||||||
|
b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
|
||||||
|
b.Assert(os.Chdir(workDir), qt.IsNil)
|
||||||
b.WithSourceFile("assets/js/main.js", mainJS)
|
b.WithSourceFile("assets/js/main.js", mainJS)
|
||||||
b.WithSourceFile("assets/js/included.js", includedJS)
|
b.WithSourceFile("assets/js/included.js", includedJS)
|
||||||
|
|
||||||
_, err = captureStdout(func() error {
|
b.Build(BuildCfg{})
|
||||||
return b.BuildE(BuildCfg{})
|
|
||||||
})
|
|
||||||
b.Assert(err, qt.IsNil)
|
|
||||||
|
|
||||||
b.AssertFileContent("public/index.html", `
|
b.AssertFileContent("public/index.html", `
|
||||||
Built: (()=>{console.log("included");console.log("main");})();
|
console.log("included");
|
||||||
`)
|
|
||||||
|
`)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,7 +45,6 @@ type Type struct {
|
||||||
|
|
||||||
Delimiter string `json:"delimiter"` // e.g. "."
|
Delimiter string `json:"delimiter"` // e.g. "."
|
||||||
|
|
||||||
// TODO(bep) make this a string to make it hashable + method
|
|
||||||
Suffixes []string `json:"suffixes"`
|
Suffixes []string `json:"suffixes"`
|
||||||
|
|
||||||
// Set when doing lookup by suffix.
|
// Set when doing lookup by suffix.
|
||||||
|
@ -130,13 +129,17 @@ var (
|
||||||
CSVType = Type{MainType: "text", SubType: "csv", Suffixes: []string{"csv"}, Delimiter: defaultDelimiter}
|
CSVType = Type{MainType: "text", SubType: "csv", Suffixes: []string{"csv"}, Delimiter: defaultDelimiter}
|
||||||
HTMLType = Type{MainType: "text", SubType: "html", Suffixes: []string{"html"}, Delimiter: defaultDelimiter}
|
HTMLType = Type{MainType: "text", SubType: "html", Suffixes: []string{"html"}, Delimiter: defaultDelimiter}
|
||||||
JavascriptType = Type{MainType: "application", SubType: "javascript", Suffixes: []string{"js"}, Delimiter: defaultDelimiter}
|
JavascriptType = Type{MainType: "application", SubType: "javascript", Suffixes: []string{"js"}, Delimiter: defaultDelimiter}
|
||||||
JSONType = Type{MainType: "application", SubType: "json", Suffixes: []string{"json"}, Delimiter: defaultDelimiter}
|
TypeScriptType = Type{MainType: "application", SubType: "typescript", Suffixes: []string{"ts"}, Delimiter: defaultDelimiter}
|
||||||
RSSType = Type{MainType: "application", SubType: "rss", mimeSuffix: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}
|
TSXType = Type{MainType: "text", SubType: "tsx", Suffixes: []string{"tsx"}, Delimiter: defaultDelimiter}
|
||||||
XMLType = Type{MainType: "application", SubType: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}
|
JSXType = Type{MainType: "text", SubType: "jsx", Suffixes: []string{"jsx"}, Delimiter: defaultDelimiter}
|
||||||
SVGType = Type{MainType: "image", SubType: "svg", mimeSuffix: "xml", Suffixes: []string{"svg"}, Delimiter: defaultDelimiter}
|
|
||||||
TextType = Type{MainType: "text", SubType: "plain", Suffixes: []string{"txt"}, Delimiter: defaultDelimiter}
|
JSONType = Type{MainType: "application", SubType: "json", Suffixes: []string{"json"}, Delimiter: defaultDelimiter}
|
||||||
TOMLType = Type{MainType: "application", SubType: "toml", Suffixes: []string{"toml"}, Delimiter: defaultDelimiter}
|
RSSType = Type{MainType: "application", SubType: "rss", mimeSuffix: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}
|
||||||
YAMLType = Type{MainType: "application", SubType: "yaml", Suffixes: []string{"yaml", "yml"}, Delimiter: defaultDelimiter}
|
XMLType = Type{MainType: "application", SubType: "xml", Suffixes: []string{"xml"}, Delimiter: defaultDelimiter}
|
||||||
|
SVGType = Type{MainType: "image", SubType: "svg", mimeSuffix: "xml", Suffixes: []string{"svg"}, Delimiter: defaultDelimiter}
|
||||||
|
TextType = Type{MainType: "text", SubType: "plain", Suffixes: []string{"txt"}, Delimiter: defaultDelimiter}
|
||||||
|
TOMLType = Type{MainType: "application", SubType: "toml", Suffixes: []string{"toml"}, Delimiter: defaultDelimiter}
|
||||||
|
YAMLType = Type{MainType: "application", SubType: "yaml", Suffixes: []string{"yaml", "yml"}, Delimiter: defaultDelimiter}
|
||||||
|
|
||||||
// Common image types
|
// Common image types
|
||||||
PNGType = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter}
|
PNGType = Type{MainType: "image", SubType: "png", Suffixes: []string{"png"}, Delimiter: defaultDelimiter}
|
||||||
|
@ -165,6 +168,9 @@ var DefaultTypes = Types{
|
||||||
SASSType,
|
SASSType,
|
||||||
HTMLType,
|
HTMLType,
|
||||||
JavascriptType,
|
JavascriptType,
|
||||||
|
TypeScriptType,
|
||||||
|
TSXType,
|
||||||
|
JSXType,
|
||||||
JSONType,
|
JSONType,
|
||||||
RSSType,
|
RSSType,
|
||||||
XMLType,
|
XMLType,
|
||||||
|
|
|
@ -40,6 +40,9 @@ func TestDefaultTypes(t *testing.T) {
|
||||||
{CSVType, "text", "csv", "csv", "text/csv", "text/csv"},
|
{CSVType, "text", "csv", "csv", "text/csv", "text/csv"},
|
||||||
{HTMLType, "text", "html", "html", "text/html", "text/html"},
|
{HTMLType, "text", "html", "html", "text/html", "text/html"},
|
||||||
{JavascriptType, "application", "javascript", "js", "application/javascript", "application/javascript"},
|
{JavascriptType, "application", "javascript", "js", "application/javascript", "application/javascript"},
|
||||||
|
{TypeScriptType, "application", "typescript", "ts", "application/typescript", "application/typescript"},
|
||||||
|
{TSXType, "text", "tsx", "tsx", "text/tsx", "text/tsx"},
|
||||||
|
{JSXType, "text", "jsx", "jsx", "text/jsx", "text/jsx"},
|
||||||
{JSONType, "application", "json", "json", "application/json", "application/json"},
|
{JSONType, "application", "json", "json", "application/json", "application/json"},
|
||||||
{RSSType, "application", "rss", "xml", "application/rss+xml", "application/rss+xml"},
|
{RSSType, "application", "rss", "xml", "application/rss+xml", "application/rss+xml"},
|
||||||
{SVGType, "image", "svg", "svg", "image/svg+xml", "image/svg+xml"},
|
{SVGType, "image", "svg", "svg", "image/svg+xml", "image/svg+xml"},
|
||||||
|
@ -58,7 +61,7 @@ func TestDefaultTypes(t *testing.T) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Assert(len(DefaultTypes), qt.Equals, 23)
|
c.Assert(len(DefaultTypes), qt.Equals, 26)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,11 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path"
|
"path"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/hugolib/filesystems"
|
"github.com/gohugoio/hugo/hugolib/filesystems"
|
||||||
|
"github.com/gohugoio/hugo/media"
|
||||||
"github.com/gohugoio/hugo/resources/internal"
|
"github.com/gohugoio/hugo/resources/internal"
|
||||||
|
|
||||||
"github.com/mitchellh/mapstructure"
|
"github.com/mitchellh/mapstructure"
|
||||||
|
@ -28,15 +31,46 @@ import (
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const defaultTarget = "esnext"
|
||||||
|
|
||||||
type Options struct {
|
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
|
Minify bool
|
||||||
Externals []string
|
|
||||||
Target string
|
Target string
|
||||||
Loader string
|
|
||||||
Defines map[string]string
|
|
||||||
JSXFactory string
|
JSXFactory string
|
||||||
JSXFragment 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) {
|
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
|
return
|
||||||
}
|
}
|
||||||
err = mapstructure.WeakDecode(m, &opts)
|
err = mapstructure.WeakDecode(m, &opts)
|
||||||
|
|
||||||
|
if opts.TargetPath != "" {
|
||||||
|
opts.TargetPath = helpers.ToSlashTrimLeading(opts.TargetPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
opts.Target = strings.ToLower(opts.Target)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -57,7 +98,7 @@ func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) *Client {
|
||||||
}
|
}
|
||||||
|
|
||||||
type buildTransformation struct {
|
type buildTransformation struct {
|
||||||
options Options
|
options internalOptions
|
||||||
rs *resources.Spec
|
rs *resources.Spec
|
||||||
sfs *filesystems.SourceFilesystem
|
sfs *filesystems.SourceFilesystem
|
||||||
}
|
}
|
||||||
|
@ -67,9 +108,17 @@ func (t *buildTransformation) Key() internal.ResourceTransformationKey {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
|
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
|
var target api.Target
|
||||||
switch t.options.Target {
|
switch t.options.Target {
|
||||||
case "", "esnext":
|
case defaultTarget:
|
||||||
target = api.ESNext
|
target = api.ESNext
|
||||||
case "es6", "es2015":
|
case "es6", "es2015":
|
||||||
target = api.ES2015
|
target = api.ES2015
|
||||||
|
@ -88,29 +137,20 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
}
|
}
|
||||||
|
|
||||||
var loader api.Loader
|
var loader api.Loader
|
||||||
switch t.options.Loader {
|
switch ctx.InMediaType.SubType {
|
||||||
case "", "js":
|
// 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
|
loader = api.LoaderJS
|
||||||
case "jsx":
|
case media.TypeScriptType.SubType:
|
||||||
loader = api.LoaderJSX
|
|
||||||
case "ts":
|
|
||||||
loader = api.LoaderTS
|
loader = api.LoaderTS
|
||||||
case "tsx":
|
case media.TSXType.SubType:
|
||||||
loader = api.LoaderTSX
|
loader = api.LoaderTSX
|
||||||
case "json":
|
case media.JSXType.SubType:
|
||||||
loader = api.LoaderJSON
|
loader = api.LoaderJSX
|
||||||
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
|
|
||||||
default:
|
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)
|
src, err := ioutil.ReadAll(ctx.From)
|
||||||
|
@ -159,8 +199,23 @@ func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx
|
||||||
return nil
|
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(
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
69
resources/resource_transformers/js/build_test.go
Normal file
69
resources/resource_transformers/js/build_test.go
Normal file
|
@ -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: "",
|
||||||
|
})
|
||||||
|
}
|
71
tpl/internal/resourcehelpers/helpers.go
Normal file
71
tpl/internal/resourcehelpers/helpers.go
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
// Copyright 2020 The Hugo Authors. All rights reserved.
|
||||||
|
//
|
||||||
|
// Portions Copyright The Go Authors.
|
||||||
|
|
||||||
|
// 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 resourcehelpers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
_errors "github.com/pkg/errors"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/common/maps"
|
||||||
|
"github.com/gohugoio/hugo/resources"
|
||||||
|
)
|
||||||
|
|
||||||
|
// We allow string or a map as the first argument in some cases.
|
||||||
|
func ResolveIfFirstArgIsString(args []interface{}) (resources.ResourceTransformer, string, bool) {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
v1, ok1 := args[0].(string)
|
||||||
|
if !ok1 {
|
||||||
|
return nil, "", false
|
||||||
|
}
|
||||||
|
v2, ok2 := args[1].(resources.ResourceTransformer)
|
||||||
|
|
||||||
|
return v2, v1, ok2
|
||||||
|
}
|
||||||
|
|
||||||
|
// This roundabout way of doing it is needed to get both pipeline behaviour and options as arguments.
|
||||||
|
func ResolveArgs(args []interface{}) (resources.ResourceTransformer, map[string]interface{}, error) {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return nil, nil, errors.New("no Resource provided in transformation")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(args) == 1 {
|
||||||
|
r, ok := args[0].(resources.ResourceTransformer)
|
||||||
|
if !ok {
|
||||||
|
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
||||||
|
}
|
||||||
|
return r, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
r, ok := args[1].(resources.ResourceTransformer)
|
||||||
|
if !ok {
|
||||||
|
if _, ok := args[1].(map[string]interface{}); !ok {
|
||||||
|
return nil, nil, fmt.Errorf("no Resource provided in transformation")
|
||||||
|
}
|
||||||
|
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
m, err := maps.ToStringMapE(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, _errors.Wrap(err, "invalid options type")
|
||||||
|
}
|
||||||
|
|
||||||
|
return r, m, nil
|
||||||
|
}
|
66
tpl/js/js.go
66
tpl/js/js.go
|
@ -15,15 +15,12 @@
|
||||||
package js
|
package js
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/maps"
|
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/resources"
|
"github.com/gohugoio/hugo/resources"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
"github.com/gohugoio/hugo/resources/resource_transformers/js"
|
"github.com/gohugoio/hugo/resources/resource_transformers/js"
|
||||||
_errors "github.com/pkg/errors"
|
"github.com/gohugoio/hugo/tpl/internal/resourcehelpers"
|
||||||
)
|
)
|
||||||
|
|
||||||
// New returns a new instance of the js-namespaced template functions.
|
// New returns a new instance of the js-namespaced template functions.
|
||||||
|
@ -41,14 +38,28 @@ type Namespace struct {
|
||||||
|
|
||||||
// Build processes the given Resource with ESBuild.
|
// Build processes the given Resource with ESBuild.
|
||||||
func (ns *Namespace) Build(args ...interface{}) (resource.Resource, error) {
|
func (ns *Namespace) Build(args ...interface{}) (resource.Resource, error) {
|
||||||
r, m, err := ns.resolveArgs(args)
|
var (
|
||||||
if err != nil {
|
r resources.ResourceTransformer
|
||||||
return nil, err
|
m map[string]interface{}
|
||||||
}
|
targetPath string
|
||||||
var options js.Options
|
err error
|
||||||
if m != nil {
|
ok bool
|
||||||
options, err = js.DecodeOptions(m)
|
)
|
||||||
|
|
||||||
|
r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
r, m, err = resourcehelpers.ResolveArgs(args)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var options js.Options
|
||||||
|
if targetPath != "" {
|
||||||
|
options.TargetPath = helpers.ToSlashTrimLeading(targetPath)
|
||||||
|
} else if m != nil {
|
||||||
|
options, err = js.DecodeOptions(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -57,34 +68,3 @@ func (ns *Namespace) Build(args ...interface{}) (resource.Resource, error) {
|
||||||
return ns.client.Process(r, options)
|
return ns.client.Process(r, options)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// This roundabout way of doing it is needed to get both pipeline behaviour and options as arguments.
|
|
||||||
// This is a copy of tpl/resources/resolveArgs
|
|
||||||
func (ns *Namespace) resolveArgs(args []interface{}) (resources.ResourceTransformer, map[string]interface{}, error) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return nil, nil, errors.New("no Resource provided in transformation")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
r, ok := args[0].(resources.ResourceTransformer)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
|
||||||
}
|
|
||||||
return r, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, ok := args[1].(resources.ResourceTransformer)
|
|
||||||
if !ok {
|
|
||||||
if _, ok := args[1].(map[string]interface{}); !ok {
|
|
||||||
return nil, nil, fmt.Errorf("no Resource provided in transformation")
|
|
||||||
}
|
|
||||||
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := maps.ToStringMapE(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, _errors.Wrap(err, "invalid options type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, m, nil
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,13 +19,14 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/tpl/internal/resourcehelpers"
|
||||||
|
|
||||||
|
"github.com/gohugoio/hugo/helpers"
|
||||||
"github.com/gohugoio/hugo/resources/postpub"
|
"github.com/gohugoio/hugo/resources/postpub"
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/common/maps"
|
|
||||||
"github.com/gohugoio/hugo/deps"
|
"github.com/gohugoio/hugo/deps"
|
||||||
"github.com/gohugoio/hugo/resources"
|
"github.com/gohugoio/hugo/resources"
|
||||||
"github.com/gohugoio/hugo/resources/resource"
|
"github.com/gohugoio/hugo/resources/resource"
|
||||||
_errors "github.com/pkg/errors"
|
|
||||||
|
|
||||||
"github.com/gohugoio/hugo/resources/resource_factories/bundler"
|
"github.com/gohugoio/hugo/resources/resource_factories/bundler"
|
||||||
"github.com/gohugoio/hugo/resources/resource_factories/create"
|
"github.com/gohugoio/hugo/resources/resource_factories/create"
|
||||||
|
@ -239,10 +240,10 @@ func (ns *Namespace) ToCSS(args ...interface{}) (resource.Resource, error) {
|
||||||
ok bool
|
ok bool
|
||||||
)
|
)
|
||||||
|
|
||||||
r, targetPath, ok = ns.resolveIfFirstArgIsString(args)
|
r, targetPath, ok = resourcehelpers.ResolveIfFirstArgIsString(args)
|
||||||
|
|
||||||
if !ok {
|
if !ok {
|
||||||
r, m, err = ns.resolveArgs(args)
|
r, m, err = resourcehelpers.ResolveArgs(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -250,7 +251,7 @@ func (ns *Namespace) ToCSS(args ...interface{}) (resource.Resource, error) {
|
||||||
|
|
||||||
var options scss.Options
|
var options scss.Options
|
||||||
if targetPath != "" {
|
if targetPath != "" {
|
||||||
options.TargetPath = targetPath
|
options.TargetPath = helpers.ToSlashTrimLeading(targetPath)
|
||||||
} else if m != nil {
|
} else if m != nil {
|
||||||
options, err = scss.DecodeOptions(m)
|
options, err = scss.DecodeOptions(m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -263,7 +264,7 @@ func (ns *Namespace) ToCSS(args ...interface{}) (resource.Resource, error) {
|
||||||
|
|
||||||
// PostCSS processes the given Resource with PostCSS
|
// PostCSS processes the given Resource with PostCSS
|
||||||
func (ns *Namespace) PostCSS(args ...interface{}) (resource.Resource, error) {
|
func (ns *Namespace) PostCSS(args ...interface{}) (resource.Resource, error) {
|
||||||
r, m, err := ns.resolveArgs(args)
|
r, m, err := resourcehelpers.ResolveArgs(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -285,7 +286,7 @@ func (ns *Namespace) PostProcess(r resource.Resource) (postpub.PostPublishedReso
|
||||||
|
|
||||||
// Babel processes the given Resource with Babel.
|
// Babel processes the given Resource with Babel.
|
||||||
func (ns *Namespace) Babel(args ...interface{}) (resource.Resource, error) {
|
func (ns *Namespace) Babel(args ...interface{}) (resource.Resource, error) {
|
||||||
r, m, err := ns.resolveArgs(args)
|
r, m, err := resourcehelpers.ResolveArgs(args)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -301,48 +302,3 @@ func (ns *Namespace) Babel(args ...interface{}) (resource.Resource, error) {
|
||||||
return ns.babelClient.Process(r, options)
|
return ns.babelClient.Process(r, options)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We allow string or a map as the first argument in some cases.
|
|
||||||
func (ns *Namespace) resolveIfFirstArgIsString(args []interface{}) (resources.ResourceTransformer, string, bool) {
|
|
||||||
if len(args) != 2 {
|
|
||||||
return nil, "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
v1, ok1 := args[0].(string)
|
|
||||||
if !ok1 {
|
|
||||||
return nil, "", false
|
|
||||||
}
|
|
||||||
v2, ok2 := args[1].(resources.ResourceTransformer)
|
|
||||||
|
|
||||||
return v2, v1, ok2
|
|
||||||
}
|
|
||||||
|
|
||||||
// This roundabout way of doing it is needed to get both pipeline behaviour and options as arguments.
|
|
||||||
func (ns *Namespace) resolveArgs(args []interface{}) (resources.ResourceTransformer, map[string]interface{}, error) {
|
|
||||||
if len(args) == 0 {
|
|
||||||
return nil, nil, errors.New("no Resource provided in transformation")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(args) == 1 {
|
|
||||||
r, ok := args[0].(resources.ResourceTransformer)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
|
||||||
}
|
|
||||||
return r, nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
r, ok := args[1].(resources.ResourceTransformer)
|
|
||||||
if !ok {
|
|
||||||
if _, ok := args[1].(map[string]interface{}); !ok {
|
|
||||||
return nil, nil, fmt.Errorf("no Resource provided in transformation")
|
|
||||||
}
|
|
||||||
return nil, nil, fmt.Errorf("type %T not supported in Resource transformations", args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
m, err := maps.ToStringMapE(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, _errors.Wrap(err, "invalid options type")
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, m, nil
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue