github.com/neohugo/neohugo@v0.123.8/resources/resource_transformers/js/js_integration_test.go (about)

     1  // Copyright 2021 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package js_test
    15  
    16  import (
    17  	"path/filepath"
    18  	"strings"
    19  	"testing"
    20  
    21  	qt "github.com/frankban/quicktest"
    22  	"github.com/neohugo/neohugo/htesting"
    23  	"github.com/neohugo/neohugo/hugolib"
    24  )
    25  
    26  func TestBuildVariants(t *testing.T) {
    27  	c := qt.New(t)
    28  
    29  	mainWithImport := `
    30  -- config.toml --
    31  disableKinds=["page", "section", "taxonomy", "term", "sitemap", "robotsTXT"]
    32  disableLiveReload = true
    33  -- assets/js/main.js --
    34  import { hello1, hello2 } from './util1';
    35  hello1();
    36  hello2();
    37  -- assets/js/util1.js --
    38  import { hello3 } from './util2';
    39  export function hello1() {
    40  	return 'abcd';
    41  }
    42  export function hello2() {
    43  	return hello3();
    44  }
    45  -- assets/js/util2.js --
    46  export function hello3() {
    47  	return 'efgh';
    48  }
    49  -- layouts/index.html --
    50  {{ $js := resources.Get "js/main.js" | js.Build }}
    51  JS Content:{{ $js.Content }}:End:
    52  
    53  			`
    54  
    55  	c.Run("Basic", func(c *qt.C) {
    56  		b := hugolib.NewIntegrationTestBuilder(hugolib.IntegrationTestConfig{T: c, NeedsOsFS: true, TxtarString: mainWithImport}).Build()
    57  
    58  		b.AssertFileContent("public/index.html", `abcd`)
    59  	})
    60  
    61  	c.Run("Edit Import", func(c *qt.C) {
    62  		b := hugolib.NewIntegrationTestBuilder(hugolib.IntegrationTestConfig{T: c, Running: true, NeedsOsFS: true, TxtarString: mainWithImport}).Build()
    63  
    64  		b.AssertFileContent("public/index.html", `abcd`)
    65  		b.EditFileReplaceFunc("assets/js/util1.js", func(s string) string { return strings.ReplaceAll(s, "abcd", "1234") }).Build()
    66  		b.AssertFileContent("public/index.html", `1234`)
    67  	})
    68  
    69  	c.Run("Edit Import Nested", func(c *qt.C) {
    70  		b := hugolib.NewIntegrationTestBuilder(hugolib.IntegrationTestConfig{T: c, Running: true, NeedsOsFS: true, TxtarString: mainWithImport}).Build()
    71  
    72  		b.AssertFileContent("public/index.html", `efgh`)
    73  		b.EditFileReplaceFunc("assets/js/util2.js", func(s string) string { return strings.ReplaceAll(s, "efgh", "1234") }).Build()
    74  		b.AssertFileContent("public/index.html", `1234`)
    75  	})
    76  }
    77  
    78  func TestBuildWithModAndNpm(t *testing.T) {
    79  	if !htesting.IsCI() {
    80  		t.Skip("skip (relative) long running modules test when running locally")
    81  	}
    82  
    83  	c := qt.New(t)
    84  
    85  	files := `
    86  -- config.toml --
    87  baseURL = "https://example.org"
    88  disableKinds=["page", "section", "taxonomy", "term", "sitemap", "robotsTXT"]
    89  [module]
    90  [[module.imports]]
    91  path="github.com/gohugoio/hugoTestProjectJSModImports"
    92  -- go.mod --
    93  module github.com/gohugoio/tests/testHugoModules
    94  
    95  go 1.16
    96  
    97  require github.com/gohugoio/hugoTestProjectJSModImports v0.10.0 // indirect
    98  -- package.json --
    99  {
   100  	"dependencies": {
   101  	"date-fns": "^2.16.1"
   102  	}
   103  }
   104  
   105  `
   106  	b := hugolib.NewIntegrationTestBuilder(
   107  		hugolib.IntegrationTestConfig{
   108  			T:               c,
   109  			NeedsOsFS:       true,
   110  			NeedsNpmInstall: true,
   111  			TxtarString:     files,
   112  			Verbose:         true,
   113  		}).Build()
   114  
   115  	b.AssertFileContent("public/js/main.js", `
   116  greeting: "greeting configured in mod2"
   117  Hello1 from mod1: $
   118  return "Hello2 from mod1";
   119  var Hugo = "Rocks!";
   120  Hello3 from mod2. Date from date-fns: ${today}
   121  Hello from lib in the main project
   122  Hello5 from mod2.
   123  var myparam = "Hugo Rocks!";
   124  shim cwd
   125  `)
   126  
   127  	// React JSX, verify the shimming.
   128  	b.AssertFileContent("public/js/like.js", filepath.FromSlash(`@v0.10.0/assets/js/shims/react.js
   129  module.exports = window.ReactDOM;
   130  `))
   131  }
   132  
   133  func TestBuildWithNpm(t *testing.T) {
   134  	if !htesting.IsCI() {
   135  		t.Skip("skip (relative) long running modules test when running locally")
   136  	}
   137  
   138  	c := qt.New(t)
   139  
   140  	files := `
   141  -- assets/js/included.js --
   142  console.log("included");
   143  -- assets/js/main.js --
   144  import "./included";
   145  	import { toCamelCase } from "to-camel-case";
   146  
   147  	console.log("main");
   148  	console.log("To camel:", toCamelCase("space case"));
   149  -- assets/js/myjsx.jsx --
   150  import * as React from 'react'
   151  import * as ReactDOM from 'react-dom'
   152  
   153  	ReactDOM.render(
   154  	<h1>Hello, world!</h1>,
   155  	document.getElementById('root')
   156  	);
   157  -- assets/js/myts.ts --
   158  function greeter(person: string) {
   159  	return "Hello, " + person;
   160  }
   161  let user = [0, 1, 2];
   162  document.body.textContent = greeter(user);
   163  -- config.toml --
   164  disablekinds = ['taxonomy', 'term', 'page']
   165  -- content/p1.md --
   166  Content.
   167  -- data/hugo.toml --
   168  slogan = "Hugo Rocks!"
   169  -- i18n/en.yaml --
   170  hello:
   171     other: "Hello"
   172  -- i18n/fr.yaml --
   173  hello:
   174     other: "Bonjour"
   175  -- layouts/index.html --
   176  {{ $options := dict "minify" false "externals" (slice "react" "react-dom") }}
   177  {{ $js := resources.Get "js/main.js" | js.Build $options }}
   178  JS:  {{ template "print" $js }}
   179  {{ $jsx := resources.Get "js/myjsx.jsx" | js.Build $options }}
   180  JSX: {{ template "print" $jsx }}
   181  {{ $ts := resources.Get "js/myts.ts" | js.Build (dict "sourcemap" "inline")}}
   182  TS: {{ template "print" $ts }}
   183  {{ $ts2 := resources.Get "js/myts.ts" | js.Build (dict "sourcemap" "external" "TargetPath" "js/myts2.js")}}
   184  TS2: {{ template "print" $ts2 }}
   185  {{ define "print" }}RelPermalink: {{.RelPermalink}}|MIME: {{ .MediaType }}|Content: {{ .Content | safeJS }}{{ end }}
   186  -- package.json --
   187  {
   188  	"scripts": {},
   189  
   190  	"dependencies": {
   191  	"to-camel-case": "1.0.0"
   192  	}
   193  }
   194  `
   195  
   196  	b := hugolib.NewIntegrationTestBuilder(
   197  		hugolib.IntegrationTestConfig{
   198  			T:               c,
   199  			NeedsOsFS:       true,
   200  			NeedsNpmInstall: true,
   201  			TxtarString:     files,
   202  		}).Build()
   203  
   204  	b.AssertFileContent("public/js/myts.js", `//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJz`)
   205  	b.AssertFileContent("public/js/myts2.js.map", `"version": 3,`)
   206  	b.AssertFileContent("public/index.html", `
   207  		console.log(&#34;included&#34;);
   208  		if (hasSpace.test(string))
   209  		var React = __toESM(__require(&#34;react&#34;));
   210  		function greeter(person) {
   211  `)
   212  }
   213  
   214  func TestBuildError(t *testing.T) {
   215  	c := qt.New(t)
   216  
   217  	filesTemplate := `
   218  -- config.toml --
   219  disableKinds=["page", "section", "taxonomy", "term", "sitemap", "robotsTXT"]
   220  -- assets/js/main.js --
   221  // A comment.
   222  import { hello1, hello2 } from './util1';
   223  hello1();
   224  hello2();
   225  -- assets/js/util1.js --
   226  /* Some
   227  comments.
   228  */
   229  import { hello3 } from './util2';
   230  export function hello1() {
   231  	return 'abcd';
   232  }
   233  export function hello2() {
   234  	return hello3();
   235  }
   236  -- assets/js/util2.js --
   237  export function hello3() {
   238  	return 'efgh';
   239  }
   240  -- layouts/index.html --
   241  {{ $js := resources.Get "js/main.js" | js.Build }}
   242  JS Content:{{ $js.Content }}:End:
   243  
   244  			`
   245  
   246  	c.Run("Import from main not found", func(c *qt.C) {
   247  		c.Parallel()
   248  		files := strings.Replace(filesTemplate, "import { hello1, hello2 }", "import { hello1, hello2, FOOBAR }", 1)
   249  		b, err := hugolib.NewIntegrationTestBuilder(hugolib.IntegrationTestConfig{T: c, NeedsOsFS: true, TxtarString: files}).BuildE()
   250  		b.Assert(err, qt.IsNotNil)
   251  		b.Assert(err.Error(), qt.Contains, `main.js:2:25": No matching export`)
   252  	})
   253  
   254  	c.Run("Import from import not found", func(c *qt.C) {
   255  		c.Parallel()
   256  		files := strings.Replace(filesTemplate, "import { hello3 } from './util2';", "import { hello3, FOOBAR } from './util2';", 1)
   257  		b, err := hugolib.NewIntegrationTestBuilder(hugolib.IntegrationTestConfig{T: c, NeedsOsFS: true, TxtarString: files}).BuildE()
   258  		b.Assert(err, qt.IsNotNil)
   259  		b.Assert(err.Error(), qt.Contains, `util1.js:4:17": No matching export in`)
   260  	})
   261  }
   262  
   263  // See issue 10527.
   264  func TestImportHugoVsESBuild(t *testing.T) {
   265  	c := qt.New(t)
   266  
   267  	for _, importSrcDir := range []string{"node_modules", "assets"} {
   268  		c.Run(importSrcDir, func(c *qt.C) {
   269  			files := `
   270  -- IMPORT_SRC_DIR/imp1/index.js --
   271  console.log("IMPORT_SRC_DIR:imp1/index.js");
   272  -- IMPORT_SRC_DIR/imp2/index.ts --
   273  console.log("IMPORT_SRC_DIR:imp2/index.ts");
   274  -- IMPORT_SRC_DIR/imp3/foo.ts --
   275  console.log("IMPORT_SRC_DIR:imp3/foo.ts");
   276  -- assets/js/main.js --
   277  import 'imp1/index.js';
   278  import 'imp2/index.js';
   279  import 'imp3/foo.js';
   280  -- layouts/index.html --
   281  {{ $js := resources.Get "js/main.js" | js.Build }}
   282  {{ $js.RelPermalink }}
   283  			`
   284  
   285  			files = strings.ReplaceAll(files, "IMPORT_SRC_DIR", importSrcDir)
   286  
   287  			b := hugolib.NewIntegrationTestBuilder(
   288  				hugolib.IntegrationTestConfig{
   289  					T:           c,
   290  					NeedsOsFS:   true,
   291  					TxtarString: files,
   292  				}).Build()
   293  
   294  			expected := `
   295  IMPORT_SRC_DIR:imp1/index.js
   296  IMPORT_SRC_DIR:imp2/index.ts
   297  IMPORT_SRC_DIR:imp3/foo.ts
   298  `
   299  			expected = strings.ReplaceAll(expected, "IMPORT_SRC_DIR", importSrcDir)
   300  
   301  			b.AssertFileContent("public/js/main.js", expected)
   302  		})
   303  	}
   304  }
   305  
   306  // See https://github.com/evanw/esbuild/issues/2745
   307  func TestPreserveLegalComments(t *testing.T) {
   308  	t.Parallel()
   309  
   310  	files := `
   311  -- assets/js/main.js --
   312  /* @license
   313   * Main license.
   314   */
   315  import * as foo from 'js/utils';
   316  console.log("Hello Main");
   317  -- assets/js/utils/index.js --
   318  export * from './util1';
   319  export * from './util2';
   320  -- assets/js/utils/util1.js --
   321  /*! License util1  */
   322  console.log("Hello 1");
   323  -- assets/js/utils/util2.js --
   324  //! License util2  */
   325  console.log("Hello 2");
   326  -- layouts/index.html --
   327  {{ $js := resources.Get "js/main.js" | js.Build (dict "minify" false) }}
   328  {{ $js.RelPermalink }}
   329  `
   330  
   331  	b := hugolib.NewIntegrationTestBuilder(
   332  		hugolib.IntegrationTestConfig{
   333  			T:           t,
   334  			NeedsOsFS:   true,
   335  			TxtarString: files,
   336  		}).Build()
   337  
   338  	b.AssertFileContent("public/js/main.js", `
   339  License util1
   340  License util2
   341  Main license
   342  
   343  	`)
   344  }
   345  
   346  // Issue #11232
   347  func TestTypeScriptExperimentalDecorators(t *testing.T) {
   348  	t.Parallel()
   349  	files := `
   350  -- hugo.toml --
   351  disableKinds = ['RSS','sitemap','taxonomy','term']
   352  -- tsconfig.json --
   353  {
   354    "compilerOptions": {
   355      "experimentalDecorators": true,
   356    }
   357  }
   358  -- assets/ts/main.ts --
   359  function addFoo(target: any) {target.prototype.foo = 'bar'}
   360  @addFoo
   361  class A {}
   362  -- layouts/index.html --
   363  {{ $opts := dict "target" "es2020" "targetPath" "js/main.js" }}
   364  {{ (resources.Get "ts/main.ts" | js.Build $opts).Publish }}
   365  `
   366  	b := hugolib.NewIntegrationTestBuilder(
   367  		hugolib.IntegrationTestConfig{
   368  			T:           t,
   369  			NeedsOsFS:   true,
   370  			TxtarString: files,
   371  		}).Build()
   372  	b.AssertFileContent("public/js/main.js", "__decorateClass")
   373  }