"github.com/gohugoio/hugo/htesting"
+ "github.com/spf13/afero"
"github.com/spf13/viper"
qt "github.com/frankban/quicktest"
"scripts": {},
"dependencies": {
- "to-camel-case": "1.0.0"
+ "to-camel-case": "1.0.0",
+ "react": "^16",
+ "react-dom": "^16"
}
}
`
`)
}
+
+func TestJSBuildGlobals(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)
+
+ 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", "")
+
+ jsDir := filepath.Join(workDir, "assets", "js")
+ b.Assert(os.MkdirAll(jsDir, 0777), qt.IsNil)
+ b.Assert(os.Chdir(workDir), qt.IsNil)
+
+ b.WithTemplates("index.html", `
+{{- $js := resources.Get "js/main-project.js" | js.Build -}}
+{{ template "print" (dict "js" $js "name" "root") }}
+
+{{- define "print" -}}
+{{ printf "rellink-%s-%s" .name .js.RelPermalink | safeHTML }}
+{{ printf "mime-%s-%s" .name .js.MediaType | safeHTML }}
+{{ printf "content-%s-%s" .name .js.Content | safeHTML }}
+{{- end -}}
+`)
+
+ b.WithSourceFile("assets/js/normal.js", `
+const name = "root-normal";
+export default name;
+`)
+ b.WithSourceFile("assets/js/main-project.js", `
+import normal from "@js/normal";
+window.normal = normal; // make sure not to tree-shake
+`)
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/index.html", `
+const name = "root-normal";
+`)
+}
+
+func TestJSBuildOverride(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)
+
+ workDir, clean, err := htesting.CreateTempDir(hugofs.Os, "hugo-test-js2")
+ c.Assert(err, qt.IsNil)
+ defer clean()
+ // workDir := "/tmp/hugo-test-js2"
+ c.Assert(os.Chdir(workDir), qt.IsNil)
+
+ cfg := viper.New()
+ cfg.Set("workingDir", workDir)
+ fs := hugofs.NewFrom(afero.NewOsFs(), cfg)
+
+ b := newTestSitesBuilder(t)
+ b.Fs = fs
+ b.WithLogger(loggers.NewWarningLogger())
+
+ realWrite := func(name string, content string) {
+ realLocation := filepath.Join(workDir, name)
+ realDir := filepath.Dir(realLocation)
+ if _, err := os.Stat(realDir); err != nil {
+ os.MkdirAll(realDir, 0777)
+ }
+ bytesContent := []byte(content)
+ // c.Assert(ioutil.WriteFile(realLocation, bytesContent, 0777), qt.IsNil)
+ c.Assert(afero.WriteFile(b.Fs.Source, realLocation, bytesContent, 0777), qt.IsNil)
+ }
+
+ realWrite("config.toml", `
+baseURL="https://example.org"
+
+[module]
+[[module.imports]]
+path="mod2"
+[[module.imports.mounts]]
+source="assets"
+target="assets"
+[[module.imports.mounts]]
+source="layouts"
+target="layouts"
+[[module.imports]]
+path="mod1"
+[[module.imports.mounts]]
+source="assets"
+target="assets"
+[[module.imports.mounts]]
+source="layouts"
+target="layouts"
+`)
+
+ realWrite("content/p1.md", `---
+layout: sample
+---
+`)
+ realWrite("themes/mod1/layouts/_default/sample.html", `
+{{- $js := resources.Get "js/main-project.js" | js.Build -}}
+{{ template "print" (dict "js" $js "name" "root") }}
+
+{{- $js = resources.Get "js/main-mod1.js" | js.Build -}}
+{{ template "print" (dict "js" $js "name" "mod1") }}
+
+{{- $js = resources.Get "js/main-mod2.js" | js.Build (dict "data" .Site.Params) -}}
+{{ template "print" (dict "js" $js "name" "mod2") }}
+
+{{- $js = resources.Get "js/main-mod2.js" | js.Build (dict "data" .Site.Params "sourceMap" "inline" "targetPath" "js/main-mod2-inline.js") -}}
+{{ template "print" (dict "js" $js "name" "mod2") }}
+
+{{- $js = resources.Get "js/main-mod2.js" | js.Build (dict "data" .Site.Params "sourceMap" "external" "targetPath" "js/main-mod2-external.js") -}}
+{{ template "print" (dict "js" $js "name" "mod2") }}
+
+{{- define "print" -}}
+{{ printf "rellink-%s-%s" .name .js.RelPermalink | safeHTML }}
+{{ printf "mime-%s-%s" .name .js.MediaType | safeHTML }}
+{{ printf "content-%s-%s" .name .js.Content | safeHTML }}
+{{- end -}}
+`)
+
+ // Override project included file
+ // This file will override the one in mod1 and mod2
+ realWrite("assets/js/override.js", `
+const name = "root-override";
+export default name;
+`)
+
+ // Add empty theme mod config files
+ realWrite("themes/mod1/config.yml", ``)
+ realWrite("themes/mod2/config.yml", ``)
+
+ // This is the main project js file.
+ // try to include @js/override which is overridden inside of project
+ // try to include @js/override-mod which is overridden in mod2
+ realWrite("assets/js/main-project.js", `
+import override from "@js/override";
+import overrideMod from "@js/override-mod";
+window.override = override; // make sure to prevent tree-shake
+window.overrideMod = overrideMod; // make sure to prevent tree-shake
+`)
+ // This is the mod1 js file
+ // try to include @js/override which is overridden inside of the project
+ // try to include @js/override-mod which is overridden in mod2
+ realWrite("themes/mod1/assets/js/main-mod1.js", `
+import override from "@js/override";
+import overrideMod from "@js/override-mod";
+window.mod = "mod1";
+window.override = override; // make sure to prevent tree-shake
+window.overrideMod = overrideMod; // make sure to prevent tree-shake
+`)
+ // This is the mod1 js file (overridden in mod2)
+ // try to include @js/override which is overridden inside of the project
+ // try to include @js/override-mod which is overridden in mod2
+ realWrite("themes/mod2/assets/js/main-mod1.js", `
+import override from "@js/override";
+import overrideMod from "@js/override-mod";
+window.mod = "mod2";
+window.override = override; // make sure to prevent tree-shake
+window.overrideMod = overrideMod; // make sure to prevent tree-shake
+`)
+ // This is mod2 js file
+ // try to include @js/override which is overridden inside of the project
+ // try to include @js/override-mod which is overridden in mod2
+ // try to include @config which is declared in a local jsconfig.json file
+ // try to include @data which was passed as "data" into js.Build
+ realWrite("themes/mod2/assets/js/main-mod2.js", `
+import override from "@js/override";
+import overrideMod from "@js/override-mod";
+import config from "@config";
+import data from "@data";
+window.data = data;
+window.override = override; // make sure to prevent tree-shake
+window.overrideMod = overrideMod; // make sure to prevent tree-shake
+window.config = config;
+`)
+ realWrite("themes/mod2/assets/js/jsconfig.json", `
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@config": ["./config.json"]
+ }
+ }
+}
+`)
+ realWrite("themes/mod2/assets/js/config.json", `
+{
+ "data": {
+ "sample": "sample"
+ }
+}
+`)
+ realWrite("themes/mod1/assets/js/override.js", `
+const name = "mod1-override";
+export default name;
+`)
+ realWrite("themes/mod2/assets/js/override.js", `
+const name = "mod2-override";
+export default name;
+`)
+ realWrite("themes/mod1/assets/js/override-mod.js", `
+const nameMod = "mod1-override";
+export default nameMod;
+`)
+ realWrite("themes/mod2/assets/js/override-mod.js", `
+const nameMod = "mod2-override";
+export default nameMod;
+`)
+ b.WithConfigFile("toml", `
+baseURL="https://example.org"
+themesDir="./themes"
+[module]
+[[module.imports]]
+path="mod2"
+[[module.imports.mounts]]
+source="assets"
+target="assets"
+[[module.imports.mounts]]
+source="layouts"
+target="layouts"
+[[module.imports]]
+path="mod1"
+[[module.imports.mounts]]
+source="assets"
+target="assets"
+[[module.imports.mounts]]
+source="layouts"
+target="layouts"
+`)
+
+ b.WithWorkingDir(workDir)
+ b.LoadConfig()
+
+ b.Build(BuildCfg{})
+
+ b.AssertFileContent("public/js/main-mod1.js", `
+name = "root-override";
+nameMod = "mod2-override";
+window.mod = "mod2";
+`)
+ b.AssertFileContent("public/js/main-mod2.js", `
+name = "root-override";
+nameMod = "mod2-override";
+sample: "sample"
+"sect"
+`)
+ b.AssertFileContent("public/js/main-project.js", `
+name = "root-override";
+nameMod = "mod2-override";
+`)
+ b.AssertFileContent("public/js/main-mod2-external.js.map", `
+const nameMod = \"mod2-override\";\nexport default nameMod;\n
+"\nimport override from \"@js/override\";\nimport overrideMod from \"@js/override-mod\";\nimport config from \"@config\";\nimport data from \"@data\";\nwindow.data = data;\nwindow.override = override; // make sure to prevent tree-shake\nwindow.overrideMod = overrideMod; // make sure to prevent tree-shake\nwindow.config = config;\n"
+`)
+ b.AssertFileContent("public/js/main-mod2-inline.js", `
+ sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsiYXNzZXRzL2pzL292ZXJyaWRlLmpzIiwgInRoZW
+`)
+}
package js
import (
+ "encoding/json"
"fmt"
"io/ioutil"
+ "os"
"path"
+ "path/filepath"
+ "reflect"
"strings"
+ "github.com/achiku/varfmt"
"github.com/spf13/cast"
"github.com/gohugoio/hugo/helpers"
"github.com/gohugoio/hugo/resources/resource"
)
+// Options esbuild configuration
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
// Whether to minify to output.
Minify bool
- // Whether to write mapfiles (currently inline only)
+ // Whether to write mapfiles
SourceMap string
// The language target.
// User defined symbols.
Defines map[string]interface{}
+ // User defined data (must be JSON marshall'able)
+ Data interface{}
+
// What to use instead of React.createElement.
JSXFactory string
contents string
sourcefile string
resolveDir string
+ workDir string
+ tsConfig string
}
func decodeOptions(m map[string]interface{}) (Options, error) {
return opts, nil
}
+// Client context for esbuild
type Client struct {
rs *resources.Spec
sfs *filesystems.SourceFilesystem
}
+// New create new client context
func New(fs *filesystems.SourceFilesystem, rs *resources.Spec) *Client {
return &Client{rs: rs, sfs: fs}
}
return internal.NewResourceTransformationKey("jsbuild", t.optsm)
}
+func appendExts(list []string, rel string) []string {
+ for _, ext := range []string{".tsx", ".ts", ".jsx", ".mjs", ".cjs", ".js", ".json"} {
+ list = append(list, fmt.Sprintf("%s/index%s", rel, ext))
+ }
+ return list
+}
+
func (t *buildTransformation) Transform(ctx *resources.ResourceTransformationCtx) error {
ctx.OutMediaType = media.JavascriptType
return err
}
- sdir, sfile := path.Split(ctx.SourcePath)
+ sdir, sfile := filepath.Split(t.sfs.RealFilename(ctx.SourcePath))
+ opts.workDir, err = filepath.Abs(t.rs.WorkingDir)
+ if err != nil {
+ return err
+ }
+
opts.sourcefile = sfile
- opts.resolveDir = t.sfs.RealFilename(sdir)
+ opts.resolveDir = sdir
opts.contents = string(src)
opts.mediaType = ctx.InMediaType
+ // Create new temporary tsconfig file
+ newTSConfig, err := ioutil.TempFile("", "tsconfig.*.json")
+ if err != nil {
+ return err
+ }
+
+ filesToDelete := make([]*os.File, 0)
+
+ defer func() {
+ for _, file := range filesToDelete {
+ os.Remove(file.Name())
+ }
+ }()
+
+ filesToDelete = append(filesToDelete, newTSConfig)
+ configDir, _ := filepath.Split(newTSConfig.Name())
+
+ // Search for the innerMost tsconfig or jsconfig
+ innerTsConfig := ""
+ tsDir := opts.resolveDir
+ baseURLAbs := configDir
+ baseURL := "."
+ for tsDir != "." {
+ tryTsConfig := path.Join(tsDir, "tsconfig.json")
+ _, err := os.Stat(tryTsConfig)
+ if err != nil {
+ tryTsConfig := path.Join(tsDir, "jsconfig.json")
+ _, err = os.Stat(tryTsConfig)
+ if err == nil {
+ innerTsConfig = tryTsConfig
+ baseURLAbs = tsDir
+ break
+ }
+ } else {
+ innerTsConfig = tryTsConfig
+ baseURLAbs = tsDir
+ break
+ }
+ if tsDir == opts.workDir {
+ break
+ }
+ tsDir = path.Dir(tsDir)
+ }
+
+ // Resolve paths for @assets and @js (@js is just an alias for assets/js)
+ dirs := make([]string, 0)
+ rootPaths := make([]string, 0)
+ for _, dir := range t.sfs.RealDirs(".") {
+ rootDir := dir
+ if !strings.HasSuffix(dir, "package.json") {
+ dirs = append(dirs, dir)
+ } else {
+ rootDir, _ = path.Split(dir)
+ }
+ nodeModules := path.Join(rootDir, "node_modules")
+ if _, err := os.Stat(nodeModules); err == nil {
+ rootPaths = append(rootPaths, nodeModules)
+ }
+ }
+
+ // Construct new temporary tsconfig file content
+ config := make(map[string]interface{})
+ if innerTsConfig != "" {
+ oldConfig, err := ioutil.ReadFile(innerTsConfig)
+ if err == nil {
+ // If there is an error, it just means there is no config file here.
+ // Since we're also using the tsConfig file path to detect where
+ // to put the temp file, this is ok.
+ err = json.Unmarshal(oldConfig, &config)
+ if err != nil {
+ return err
+ }
+ }
+ }
+
+ if config["compilerOptions"] == nil {
+ config["compilerOptions"] = map[string]interface{}{}
+ }
+
+ // Assign new global paths to the config file while reading existing ones.
+ compilerOptions := config["compilerOptions"].(map[string]interface{})
+
+ // Handle original baseUrl if it's there
+ if compilerOptions["baseUrl"] != nil {
+ baseURL = compilerOptions["baseUrl"].(string)
+ oldBaseURLAbs := path.Join(tsDir, baseURL)
+ rel, _ := filepath.Rel(configDir, oldBaseURLAbs)
+ configDir = oldBaseURLAbs
+ baseURLAbs = configDir
+ if "/" != helpers.FilePathSeparator {
+ // On windows we need to use slashes instead of backslash
+ rel = strings.ReplaceAll(rel, helpers.FilePathSeparator, "/")
+ }
+ if rel != "" {
+ if strings.HasPrefix(rel, ".") {
+ baseURL = rel
+ } else {
+ baseURL = fmt.Sprintf("./%s", rel)
+ }
+ }
+ compilerOptions["baseUrl"] = baseURL
+ } else {
+ compilerOptions["baseUrl"] = baseURL
+ }
+
+ jsRel := func(refPath string) string {
+ rel, _ := filepath.Rel(configDir, refPath)
+ if "/" != helpers.FilePathSeparator {
+ // On windows we need to use slashes instead of backslash
+ rel = strings.ReplaceAll(rel, helpers.FilePathSeparator, "/")
+ }
+ if rel != "" {
+ if !strings.HasPrefix(rel, ".") {
+ rel = fmt.Sprintf("./%s", rel)
+ }
+ } else {
+ rel = "."
+ }
+ return rel
+ }
+
+ // Handle possible extends
+ if config["extends"] != nil {
+ extends := config["extends"].(string)
+ extendsAbs := path.Join(tsDir, extends)
+ rel := jsRel(extendsAbs)
+ config["extends"] = rel
+ }
+
+ var optionsPaths map[string]interface{}
+ // Get original paths if they exist
+ if compilerOptions["paths"] != nil {
+ optionsPaths = compilerOptions["paths"].(map[string]interface{})
+ } else {
+ optionsPaths = make(map[string]interface{})
+ }
+ compilerOptions["paths"] = optionsPaths
+
+ assets := make([]string, 0)
+ assetsExact := make([]string, 0)
+ js := make([]string, 0)
+ jsExact := make([]string, 0)
+ for _, dir := range dirs {
+ rel := jsRel(dir)
+ assets = append(assets, fmt.Sprintf("%s/*", rel))
+ assetsExact = appendExts(assetsExact, rel)
+
+ rel = jsRel(filepath.Join(dir, "js"))
+ js = append(js, fmt.Sprintf("%s/*", rel))
+ jsExact = appendExts(jsExact, rel)
+ }
+
+ optionsPaths["@assets/*"] = assets
+ optionsPaths["@js/*"] = js
+
+ // Make @js and @assets absolue matches search for index files
+ // to get around the problem in ESBuild resolving folders as index files.
+ optionsPaths["@assets"] = assetsExact
+ optionsPaths["@js"] = jsExact
+
+ var newDataFile *os.File
+ if opts.Data != nil {
+ // Create a data file
+ lines := make([]string, 0)
+ lines = append(lines, "// auto generated data import")
+ exports := make([]string, 0)
+ keys := make(map[string]bool)
+
+ var bytes []byte
+
+ conv := reflect.ValueOf(opts.Data)
+ convType := conv.Kind()
+ if convType == reflect.Interface {
+ if conv.IsNil() {
+ conv = reflect.Value{}
+ }
+ }
+
+ if conv.Kind() != reflect.Map {
+ // Write out as single JSON file
+ newDataFile, err = ioutil.TempFile("", "data.*.json")
+ // Output the data
+ bytes, err = json.MarshalIndent(conv.InterfaceData(), "", " ")
+ if err != nil {
+ return err
+ }
+ } else {
+ // Try to allow tree shaking at the root
+ newDataFile, err = ioutil.TempFile(configDir, "data.*.js")
+ for _, key := range conv.MapKeys() {
+ strKey := key.Interface().(string)
+ if keys[strKey] {
+ continue
+ }
+ keys[strKey] = true
+
+ value := conv.MapIndex(key)
+
+ keyVar := varfmt.PublicVarName(strKey)
+
+ // Output the data
+ bytes, err := json.MarshalIndent(value.Interface(), "", " ")
+ if err != nil {
+ return err
+ }
+ jsonValue := string(bytes)
+
+ lines = append(lines, fmt.Sprintf("export const %s = %s;", keyVar, jsonValue))
+ exports = append(exports, fmt.Sprintf(" %s,", keyVar))
+ if strKey != keyVar {
+ exports = append(exports, fmt.Sprintf(" [\"%s\"]: %s,", strKey, keyVar))
+ }
+ }
+
+ lines = append(lines, "const all = {")
+ for _, line := range exports {
+ lines = append(lines, line)
+ }
+ lines = append(lines, "};")
+ lines = append(lines, "export default all;")
+
+ bytes = []byte(strings.Join(lines, "\n"))
+ }
+
+ // Write tsconfig file
+ _, err = newDataFile.Write(bytes)
+ if err != nil {
+ return err
+ }
+ err = newDataFile.Close()
+ if err != nil {
+ return err
+ }
+
+ // Link this file into `import data from "@data"`
+ dataFiles := make([]string, 1)
+ rel, _ := filepath.Rel(baseURLAbs, newDataFile.Name())
+ dataFiles[0] = rel
+ optionsPaths["@data"] = dataFiles
+
+ filesToDelete = append(filesToDelete, newDataFile)
+ }
+
+ if len(rootPaths) > 0 {
+ // This will allow import "react" to resolve a react module that's
+ // either in the root node_modules or in one of the hugo mods.
+ optionsPaths["*"] = rootPaths
+ }
+
+ // Output the new config file
+ bytes, err := json.MarshalIndent(config, "", " ")
+ if err != nil {
+ return err
+ }
+
+ // Write tsconfig file
+ _, err = newTSConfig.Write(bytes)
+ if err != nil {
+ return err
+ }
+ err = newTSConfig.Close()
+ if err != nil {
+ return err
+ }
+
+ // Tell ESBuild about this new config file to use
+ opts.tsConfig = newTSConfig.Name()
+
buildOptions, err := toBuildOptions(opts)
if err != nil {
+ os.Remove(opts.tsConfig)
return err
}
result := api.Build(buildOptions)
+
+ if len(result.Warnings) > 0 {
+ for _, value := range result.Warnings {
+ if value.Location != nil {
+ t.rs.Logger.WARN.Println(fmt.Sprintf("%s:%d: WARN: %s",
+ filepath.Join(sdir, value.Location.File),
+ value.Location.Line, value.Text))
+ t.rs.Logger.WARN.Println(" ", value.Location.LineText)
+ } else {
+ t.rs.Logger.WARN.Println(fmt.Sprintf("%s: WARN: %s",
+ sdir,
+ value.Text))
+ }
+ }
+ }
if len(result.Errors) > 0 {
- return fmt.Errorf("%s", result.Errors[0].Text)
+ output := result.Errors[0].Text
+ for _, value := range result.Errors {
+ var line string
+ if value.Location != nil {
+ line = fmt.Sprintf("%s:%d ERROR: %s",
+ filepath.Join(sdir, value.Location.File),
+ value.Location.Line, value.Text)
+ } else {
+ line = fmt.Sprintf("%s ERROR: %s",
+ sdir,
+ value.Text)
+ }
+ t.rs.Logger.ERROR.Println(line)
+ output = fmt.Sprintf("%s\n%s", output, line)
+ if value.Location != nil {
+ t.rs.Logger.ERROR.Println(" ", value.Location.LineText)
+ }
+ }
+ return fmt.Errorf("%s", output)
+ }
+
+ if buildOptions.Outfile != "" {
+ _, tfile := path.Split(opts.TargetPath)
+ output := fmt.Sprintf("%s//# sourceMappingURL=%s\n",
+ string(result.OutputFiles[1].Contents), tfile+".map")
+ _, err := ctx.To.Write([]byte(output))
+ if err != nil {
+ return err
+ }
+ ctx.PublishSourceMap(string(result.OutputFiles[0].Contents))
+ } else {
+ ctx.To.Write(result.OutputFiles[0].Contents)
}
- ctx.To.Write(result.OutputFiles[0].Contents)
return nil
}
+// Process process esbuild transform
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},
default:
err = fmt.Errorf("unsupported script output format: %q", opts.Format)
return
-
}
var defines map[string]string
defines = cast.ToStringMapString(opts.Defines)
}
+ // By default we only need to specify outDir and no outFile
+ var outDir = opts.outDir
+ var outFile = ""
var sourceMap api.SourceMap
switch opts.SourceMap {
case "inline":
sourceMap = api.SourceMapInline
+ case "external":
+ // When doing external sourcemaps we should specify
+ // out file and no out dir
+ sourceMap = api.SourceMapExternal
+ outFile = filepath.Join(opts.workDir, opts.TargetPath)
+ outDir = ""
case "":
sourceMap = api.SourceMapNone
default:
}
buildOptions = api.BuildOptions{
- Outfile: "",
+ Outfile: outFile,
Bundle: true,
Target: target,
MinifyIdentifiers: opts.Minify,
MinifySyntax: opts.Minify,
- Outdir: opts.outDir,
+ Outdir: outDir,
Defines: defines,
Externals: opts.Externals,
JSXFactory: opts.JSXFactory,
JSXFragment: opts.JSXFragment,
- //Tsconfig: opts.TSConfig,
+ Tsconfig: opts.tsConfig,
Stdin: &api.StdinOptions{
Contents: opts.contents,