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("included"); 208 if (hasSpace.test(string)) 209 var React = __toESM(__require("react")); 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 }