github.com/neohugo/neohugo@v0.123.8/hugolib/filesystems/basefs_test.go (about) 1 // Copyright 2024 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 filesystems_test 15 16 import ( 17 "errors" 18 "fmt" 19 "os" 20 "path/filepath" 21 "runtime" 22 "strings" 23 "testing" 24 25 "github.com/neohugo/neohugo/config" 26 "github.com/neohugo/neohugo/config/testconfig" 27 "github.com/neohugo/neohugo/hugolib" 28 29 "github.com/spf13/afero" 30 31 qt "github.com/frankban/quicktest" 32 "github.com/neohugo/neohugo/hugofs" 33 "github.com/neohugo/neohugo/hugolib/filesystems" 34 "github.com/neohugo/neohugo/hugolib/paths" 35 ) 36 37 func TestNewBaseFs(t *testing.T) { 38 c := qt.New(t) 39 v := config.New() 40 41 themes := []string{"btheme", "atheme"} 42 43 workingDir := filepath.FromSlash("/my/work") 44 v.Set("workingDir", workingDir) 45 v.Set("contentDir", "content") 46 v.Set("themesDir", "themes") 47 v.Set("defaultContentLanguage", "en") 48 v.Set("theme", themes[:1]) 49 v.Set("publishDir", "public") 50 51 afs := afero.NewMemMapFs() 52 53 // Write some data to the themes 54 for _, theme := range themes { 55 for _, dir := range []string{"i18n", "data", "archetypes", "layouts"} { 56 base := filepath.Join(workingDir, "themes", theme, dir) 57 filenameTheme := filepath.Join(base, fmt.Sprintf("theme-file-%s.txt", theme)) 58 filenameOverlap := filepath.Join(base, "f3.txt") 59 afs.Mkdir(base, 0o755) // nolint 60 content := []byte(fmt.Sprintf("content:%s:%s", theme, dir)) 61 afero.WriteFile(afs, filenameTheme, content, 0o755) // nolint 62 afero.WriteFile(afs, filenameOverlap, content, 0o755) // nolint 63 } 64 // Write some files to the root of the theme 65 base := filepath.Join(workingDir, "themes", theme) 66 // nolint 67 afero.WriteFile(afs, filepath.Join(base, fmt.Sprintf("theme-root-%s.txt", theme)), []byte(fmt.Sprintf("content:%s", theme)), 0o755) 68 // nolint 69 afero.WriteFile(afs, filepath.Join(base, "file-theme-root.txt"), []byte(fmt.Sprintf("content:%s", theme)), 0o755) 70 } 71 // nolint 72 afero.WriteFile(afs, filepath.Join(workingDir, "file-root.txt"), []byte("content-project"), 0o755) 73 // nolint 74 afero.WriteFile(afs, filepath.Join(workingDir, "themes", "btheme", "config.toml"), []byte(` 75 theme = ["atheme"] 76 `), 0o755) 77 78 setConfigAndWriteSomeFilesTo(afs, v, "contentDir", "mycontent", 3) // nolint 79 setConfigAndWriteSomeFilesTo(afs, v, "i18nDir", "myi18n", 4) // nolint 80 setConfigAndWriteSomeFilesTo(afs, v, "layoutDir", "mylayouts", 5) // nolint 81 setConfigAndWriteSomeFilesTo(afs, v, "staticDir", "mystatic", 6) // nolint 82 setConfigAndWriteSomeFilesTo(afs, v, "dataDir", "mydata", 7) // nolint 83 setConfigAndWriteSomeFilesTo(afs, v, "archetypeDir", "myarchetypes", 8) // nolint 84 setConfigAndWriteSomeFilesTo(afs, v, "assetDir", "myassets", 9) // nolint 85 setConfigAndWriteSomeFilesTo(afs, v, "resourceDir", "myrsesource", 10) // nolint 86 87 conf := testconfig.GetTestConfig(afs, v) 88 fs := hugofs.NewFrom(afs, conf.BaseConfig()) 89 90 p, err := paths.New(fs, conf) 91 c.Assert(err, qt.IsNil) 92 93 bfs, err := filesystems.NewBase(p, nil) 94 c.Assert(err, qt.IsNil) 95 c.Assert(bfs, qt.Not(qt.IsNil)) 96 97 root, err := bfs.I18n.Fs.Open("") 98 c.Assert(err, qt.IsNil) 99 dirnames, err := root.Readdirnames(-1) 100 c.Assert(err, qt.IsNil) 101 c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"}) 102 103 root, err = bfs.Data.Fs.Open("") 104 c.Assert(err, qt.IsNil) 105 dirnames, err = root.Readdirnames(-1) 106 c.Assert(err, qt.IsNil) 107 c.Assert(dirnames, qt.DeepEquals, []string{"f1.txt", "f2.txt", "f3.txt", "f4.txt", "f5.txt", "f6.txt", "f7.txt", "f3.txt", "theme-file-btheme.txt", "f3.txt", "theme-file-atheme.txt"}) 108 109 checkFileCount(bfs.Layouts.Fs, "", c, 7) 110 111 checkFileCount(bfs.Content.Fs, "", c, 3) 112 checkFileCount(bfs.I18n.Fs, "", c, 8) // 4 + 4 themes 113 114 checkFileCount(bfs.Static[""].Fs, "", c, 6) 115 checkFileCount(bfs.Data.Fs, "", c, 11) // 7 + 4 themes 116 checkFileCount(bfs.Archetypes.Fs, "", c, 10) // 8 + 2 themes 117 checkFileCount(bfs.Assets.Fs, "", c, 9) 118 checkFileCount(bfs.Work, "", c, 90) 119 120 c.Assert(bfs.IsStatic(filepath.Join(workingDir, "mystatic", "file1.txt")), qt.Equals, true) 121 122 contentFilename := filepath.Join(workingDir, "mycontent", "file1.txt") 123 c.Assert(bfs.IsContent(contentFilename), qt.Equals, true) 124 // Check Work fs vs theme 125 checkFileContent(bfs.Work, "file-root.txt", c, "content-project") 126 checkFileContent(bfs.Work, "theme-root-atheme.txt", c, "content:atheme") 127 128 // https://github.com/neohugo/neohugo/issues/5318 129 // Check both project and theme. 130 for _, fs := range []afero.Fs{bfs.Archetypes.Fs, bfs.Layouts.Fs} { 131 for _, filename := range []string{"/f1.txt", "/theme-file-atheme.txt"} { 132 filename = filepath.FromSlash(filename) 133 f, err := fs.Open(filename) 134 c.Assert(err, qt.IsNil) 135 f.Close() 136 } 137 } 138 } 139 140 func TestNewBaseFsEmpty(t *testing.T) { 141 c := qt.New(t) 142 afs := afero.NewMemMapFs() 143 conf := testconfig.GetTestConfig(afs, nil) 144 fs := hugofs.NewFrom(afs, conf.BaseConfig()) 145 p, err := paths.New(fs, conf) 146 c.Assert(err, qt.IsNil) 147 bfs, err := filesystems.NewBase(p, nil) 148 c.Assert(err, qt.IsNil) 149 c.Assert(bfs, qt.Not(qt.IsNil)) 150 c.Assert(bfs.Archetypes.Fs, qt.Not(qt.IsNil)) 151 c.Assert(bfs.Layouts.Fs, qt.Not(qt.IsNil)) 152 c.Assert(bfs.Data.Fs, qt.Not(qt.IsNil)) 153 c.Assert(bfs.I18n.Fs, qt.Not(qt.IsNil)) 154 c.Assert(bfs.Work, qt.Not(qt.IsNil)) 155 c.Assert(bfs.Content.Fs, qt.Not(qt.IsNil)) 156 c.Assert(bfs.Static, qt.Not(qt.IsNil)) 157 } 158 159 func TestRealDirs(t *testing.T) { 160 c := qt.New(t) 161 v := config.New() 162 root, themesDir := t.TempDir(), t.TempDir() 163 v.Set("workingDir", root) 164 v.Set("themesDir", themesDir) 165 v.Set("assetDir", "myassets") 166 v.Set("theme", "mytheme") 167 168 afs := &hugofs.OpenFilesFs{Fs: hugofs.Os} 169 170 c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf1"), 0o755), qt.IsNil) 171 c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "scss", "sf2"), 0o755), qt.IsNil) 172 c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2"), 0o755), qt.IsNil) 173 c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3"), 0o755), qt.IsNil) 174 c.Assert(afs.MkdirAll(filepath.Join(root, "resources"), 0o755), qt.IsNil) 175 c.Assert(afs.MkdirAll(filepath.Join(themesDir, "mytheme", "resources"), 0o755), qt.IsNil) 176 177 c.Assert(afs.MkdirAll(filepath.Join(root, "myassets", "js", "f2"), 0o755), qt.IsNil) 178 179 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf1", "a1.scss")), []byte("content"), 0o755) // nolint 180 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "sf2", "a3.scss")), []byte("content"), 0o755) // nolint 181 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "scss", "a2.scss")), []byte("content"), 0o755) // nolint 182 afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf2", "a3.scss")), []byte("content"), 0o755) // nolint 183 afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "assets", "scss", "sf3", "a4.scss")), []byte("content"), 0o755) // nolint 184 185 afero.WriteFile(afs, filepath.Join(filepath.Join(themesDir, "mytheme", "resources", "t1.txt")), []byte("content"), 0o755) // nolint 186 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p1.txt")), []byte("content"), 0o755) // nolint 187 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "resources", "p2.txt")), []byte("content"), 0o755) // nolint 188 189 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "f2", "a1.js")), []byte("content"), 0o755) // nolint 190 afero.WriteFile(afs, filepath.Join(filepath.Join(root, "myassets", "js", "a2.js")), []byte("content"), 0o755) // nolint 191 192 conf := testconfig.GetTestConfig(afs, v) 193 fs := hugofs.NewFrom(afs, conf.BaseConfig()) 194 p, err := paths.New(fs, conf) 195 c.Assert(err, qt.IsNil) 196 bfs, err := filesystems.NewBase(p, nil) 197 c.Assert(err, qt.IsNil) 198 c.Assert(bfs, qt.Not(qt.IsNil)) 199 200 checkFileCount(bfs.Assets.Fs, "", c, 6) 201 202 realDirs := bfs.Assets.RealDirs("scss") 203 c.Assert(len(realDirs), qt.Equals, 2) 204 c.Assert(realDirs[0], qt.Equals, filepath.Join(root, "myassets/scss")) 205 c.Assert(realDirs[len(realDirs)-1], qt.Equals, filepath.Join(themesDir, "mytheme/assets/scss")) 206 207 realDirs = bfs.Assets.RealDirs("foo") 208 c.Assert(len(realDirs), qt.Equals, 0) 209 210 c.Assert(afs.OpenFiles(), qt.HasLen, 0) 211 } 212 213 func TestWatchFilenames(t *testing.T) { 214 t.Parallel() 215 files := ` 216 -- hugo.toml -- 217 theme = "t1" 218 [[module.mounts]] 219 source = 'content' 220 target = 'content' 221 [[module.mounts]] 222 source = 'content2' 223 target = 'content/c2' 224 [[module.mounts]] 225 source = "hugo_stats.json" 226 target = "assets/watching/hugo_stats.json" 227 -- hugo_stats.json -- 228 Some stats. 229 -- content/foo.md -- 230 foo 231 -- content2/bar.md -- 232 -- themes/t1/layouts/_default/single.html -- 233 {{ .Content }} 234 -- themes/t1/static/f1.txt -- 235 ` 236 b := hugolib.Test(t, files) 237 bfs := b.H.BaseFs 238 watchFilenames := bfs.WatchFilenames() 239 // []string{"/hugo_stats.json", "/content", "/content2", "/themes/t1/layouts", "/themes/t1/layouts/_default", "/themes/t1/static"} 240 b.Assert(watchFilenames, qt.HasLen, 6) 241 } 242 243 func TestNoSymlinks(t *testing.T) { 244 if runtime.GOOS == "windows" { 245 t.Skip("skip on Windows") 246 } 247 files := ` 248 -- hugo.toml -- 249 theme = "t1" 250 -- content/a/foo.md -- 251 foo 252 -- static/a/f1.txt -- 253 F1 text 254 -- themes/t1/layouts/_default/single.html -- 255 {{ .Content }} 256 -- themes/t1/static/a/f1.txt -- 257 ` 258 tmpDir := t.TempDir() 259 260 wd, _ := os.Getwd() 261 262 for _, component := range []string{"content", "static"} { 263 aDir := filepath.Join(tmpDir, component, "a") 264 bDir := filepath.Join(tmpDir, component, "b") 265 os.MkdirAll(aDir, 0o755) // nolint 266 os.MkdirAll(bDir, 0o755) // nolint 267 os.Chdir(bDir) // nolint 268 os.Symlink("../a", "c") // nolint 269 } 270 271 os.Chdir(wd) // nolint 272 273 b := hugolib.NewIntegrationTestBuilder( 274 hugolib.IntegrationTestConfig{ 275 T: t, 276 TxtarString: files, 277 NeedsOsFS: true, 278 WorkingDir: tmpDir, 279 }, 280 ).Build() 281 282 bfs := b.H.BaseFs 283 watchFilenames := bfs.WatchFilenames() 284 b.Assert(watchFilenames, qt.HasLen, 10) 285 } 286 287 func TestStaticFs(t *testing.T) { 288 c := qt.New(t) 289 v := config.New() 290 workDir := "mywork" 291 v.Set("workingDir", workDir) 292 v.Set("themesDir", "themes") 293 v.Set("staticDir", "mystatic") 294 v.Set("theme", []string{"t1", "t2"}) 295 296 afs := afero.NewMemMapFs() 297 298 themeStaticDir := filepath.Join(workDir, "themes", "t1", "static") 299 themeStaticDir2 := filepath.Join(workDir, "themes", "t2", "static") 300 301 afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0o755) // nolint 302 afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0o755) // nolint 303 afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0o755) // nolint 304 afero.WriteFile(afs, filepath.Join(themeStaticDir2, "f2.txt"), []byte("Hugo Themes Rocks in t2!"), 0o755) // nolint 305 306 conf := testconfig.GetTestConfig(afs, v) 307 fs := hugofs.NewFrom(afs, conf.BaseConfig()) 308 p, err := paths.New(fs, conf) 309 310 c.Assert(err, qt.IsNil) 311 bfs, err := filesystems.NewBase(p, nil) 312 c.Assert(err, qt.IsNil) 313 314 sfs := bfs.StaticFs("en") 315 316 checkFileContent(sfs, "f1.txt", c, "Hugo Rocks!") 317 checkFileContent(sfs, "f2.txt", c, "Hugo Themes Still Rocks!") 318 } 319 320 func TestStaticFsMultiHost(t *testing.T) { 321 c := qt.New(t) 322 v := config.New() 323 workDir := "mywork" 324 v.Set("workingDir", workDir) 325 v.Set("themesDir", "themes") 326 v.Set("staticDir", "mystatic") 327 v.Set("theme", "t1") 328 v.Set("defaultContentLanguage", "en") 329 330 langConfig := map[string]any{ 331 "no": map[string]any{ 332 "staticDir": "static_no", 333 "baseURL": "https://example.org/no/", 334 }, 335 "en": map[string]any{ 336 "baseURL": "https://example.org/en/", 337 }, 338 } 339 340 v.Set("languages", langConfig) 341 342 afs := afero.NewMemMapFs() 343 344 themeStaticDir := filepath.Join(workDir, "themes", "t1", "static") 345 346 afero.WriteFile(afs, filepath.Join(workDir, "mystatic", "f1.txt"), []byte("Hugo Rocks!"), 0o755) // nolint 347 afero.WriteFile(afs, filepath.Join(workDir, "static_no", "f1.txt"), []byte("Hugo Rocks in Norway!"), 0o755) // nolint 348 349 afero.WriteFile(afs, filepath.Join(themeStaticDir, "f1.txt"), []byte("Hugo Themes Rocks!"), 0o755) // nolint 350 afero.WriteFile(afs, filepath.Join(themeStaticDir, "f2.txt"), []byte("Hugo Themes Still Rocks!"), 0o755) // nolint 351 352 conf := testconfig.GetTestConfig(afs, v) 353 fs := hugofs.NewFrom(afs, conf.BaseConfig()) 354 355 p, err := paths.New(fs, conf) 356 c.Assert(err, qt.IsNil) 357 bfs, err := filesystems.NewBase(p, nil) 358 c.Assert(err, qt.IsNil) 359 enFs := bfs.StaticFs("en") 360 checkFileContent(enFs, "f1.txt", c, "Hugo Rocks!") 361 checkFileContent(enFs, "f2.txt", c, "Hugo Themes Still Rocks!") 362 363 noFs := bfs.StaticFs("no") 364 checkFileContent(noFs, "f1.txt", c, "Hugo Rocks in Norway!") 365 checkFileContent(noFs, "f2.txt", c, "Hugo Themes Still Rocks!") 366 } 367 368 func TestMakePathRelative(t *testing.T) { 369 files := ` 370 -- hugo.toml -- 371 [[module.mounts]] 372 source = "bar.txt" 373 target = "assets/foo/baz.txt" 374 [[module.imports]] 375 path = "t1" 376 [[module.imports.mounts]] 377 source = "src" 378 target = "assets/foo/bar" 379 -- bar.txt -- 380 Bar. 381 -- themes/t1/src/main.js -- 382 Main. 383 ` 384 b := hugolib.Test(t, files) 385 386 rel, found := b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/themes/t1/src/main.js"), true) 387 b.Assert(found, qt.Equals, true) 388 b.Assert(rel, qt.Equals, filepath.FromSlash("foo/bar/main.js")) 389 390 rel, found = b.H.BaseFs.Assets.MakePathRelative(filepath.FromSlash("/bar.txt"), true) 391 b.Assert(found, qt.Equals, true) 392 b.Assert(rel, qt.Equals, filepath.FromSlash("foo/baz.txt")) 393 } 394 395 func TestAbsProjectContentDir(t *testing.T) { 396 tempDir := t.TempDir() 397 398 files := ` 399 -- hugo.toml -- 400 [[module.mounts]] 401 source = "content" 402 target = "content" 403 -- content/foo.md -- 404 --- 405 title: "Foo" 406 --- 407 ` 408 409 b := hugolib.NewIntegrationTestBuilder( 410 hugolib.IntegrationTestConfig{ 411 T: t, 412 WorkingDir: tempDir, 413 TxtarString: files, 414 }, 415 ).Build() 416 417 abs1 := filepath.Join(tempDir, "content", "foo.md") 418 rel, abs2, err := b.H.BaseFs.AbsProjectContentDir("foo.md") 419 b.Assert(err, qt.IsNil) 420 b.Assert(abs2, qt.Equals, abs1) 421 b.Assert(rel, qt.Equals, filepath.FromSlash("foo.md")) 422 rel2, abs3, err := b.H.BaseFs.AbsProjectContentDir(abs1) 423 b.Assert(err, qt.IsNil) 424 b.Assert(abs3, qt.Equals, abs1) 425 b.Assert(rel2, qt.Equals, rel) 426 } 427 428 func TestContentReverseLookup(t *testing.T) { 429 files := ` 430 -- README.md -- 431 --- 432 title: README 433 --- 434 -- blog/b1.md -- 435 --- 436 title: b1 437 --- 438 -- docs/d1.md -- 439 --- 440 title: d1 441 --- 442 -- hugo.toml -- 443 baseURL = "https://example.com/" 444 [module] 445 [[module.mounts]] 446 source = "layouts" 447 target = "layouts" 448 [[module.mounts]] 449 source = "README.md" 450 target = "content/_index.md" 451 [[module.mounts]] 452 source = "blog" 453 target = "content/posts" 454 [[module.mounts]] 455 source = "docs" 456 target = "content/mydocs" 457 -- layouts/index.html -- 458 Home. 459 460 ` 461 b := hugolib.Test(t, files) 462 463 b.AssertFileContent("public/index.html", "Home.") 464 465 stat := func(path string) hugofs.FileMetaInfo { 466 ps, err := b.H.BaseFs.Content.ReverseLookup(filepath.FromSlash(path), true) 467 b.Assert(err, qt.IsNil) 468 b.Assert(ps, qt.HasLen, 1) 469 first := ps[0] 470 fi, err := b.H.BaseFs.Content.Fs.Stat(filepath.FromSlash(first.Path)) 471 b.Assert(err, qt.IsNil) 472 b.Assert(fi, qt.Not(qt.IsNil)) 473 return fi.(hugofs.FileMetaInfo) 474 } 475 476 sfs := b.H.Fs.Source 477 478 _, err := sfs.Stat("blog/b1.md") 479 b.Assert(err, qt.Not(qt.IsNil)) 480 481 _ = stat("blog/b1.md") 482 } 483 484 func TestReverseLookupShouldOnlyConsiderFilesInCurrentComponent(t *testing.T) { 485 files := ` 486 -- hugo.toml -- 487 baseURL = "https://example.com/" 488 [module] 489 [[module.mounts]] 490 source = "files/layouts" 491 target = "layouts" 492 [[module.mounts]] 493 source = "files/layouts/assets" 494 target = "assets" 495 -- files/layouts/l1.txt -- 496 l1 497 -- files/layouts/assets/l2.txt -- 498 l2 499 ` 500 b := hugolib.Test(t, files) 501 502 assetsFs := b.H.Assets 503 504 for _, checkExists := range []bool{false, true} { 505 cps, err := assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/assets/l2.txt"), checkExists) 506 b.Assert(err, qt.IsNil) 507 b.Assert(cps, qt.HasLen, 1) 508 cps, err = assetsFs.ReverseLookup(filepath.FromSlash("files/layouts/l2.txt"), checkExists) 509 b.Assert(err, qt.IsNil) 510 b.Assert(cps, qt.HasLen, 0) 511 } 512 } 513 514 func TestAssetsIssue12175(t *testing.T) { 515 files := ` 516 -- hugo.toml -- 517 baseURL = "https://example.com/" 518 [module] 519 [[module.mounts]] 520 source = "node_modules/@foo/core/assets" 521 target = "assets" 522 [[module.mounts]] 523 source = "assets" 524 target = "assets" 525 -- node_modules/@foo/core/assets/js/app.js -- 526 JS. 527 -- node_modules/@foo/core/assets/scss/app.scss -- 528 body { color: red; } 529 -- assets/scss/app.scss -- 530 body { color: blue; } 531 -- layouts/index.html -- 532 Home. 533 SCSS: {{ with resources.Get "scss/app.scss" }}{{ .RelPermalink }}|{{ .Content }}{{ end }}| 534 # Note that the pattern below will match 2 resources, which doesn't make much sense, 535 # but is how the current (and also < v0.123.0) merge logic works, and for most practical purposes, it doesn't matter. 536 SCSS Match: {{ with resources.Match "**.scss" }}{{ . | len }}|{{ range .}}{{ .RelPermalink }}|{{ end }}{{ end }}| 537 538 ` 539 540 b := hugolib.Test(t, files) 541 542 b.AssertFileContent("public/index.html", ` 543 SCSS: /scss/app.scss|body { color: blue; }| 544 SCSS Match: 2| 545 `) 546 } 547 548 func TestStaticComposite(t *testing.T) { 549 files := ` 550 -- hugo.toml -- 551 disableKinds = ["taxonomy", "term"] 552 [module] 553 [[module.mounts]] 554 source = "myfiles/f1.txt" 555 target = "static/files/f1.txt" 556 [[module.mounts]] 557 source = "f3.txt" 558 target = "static/f3.txt" 559 [[module.mounts]] 560 source = "static" 561 target = "static" 562 -- static/files/f2.txt -- 563 f2 564 -- myfiles/f1.txt -- 565 f1 566 -- f3.txt -- 567 f3 568 -- layouts/home.html -- 569 Home. 570 571 ` 572 b := hugolib.Test(t, files) 573 574 b.AssertFs(b.H.BaseFs.StaticFs(""), ` 575 . true 576 f3.txt false 577 files true 578 files/f1.txt false 579 files/f2.txt false 580 `) 581 } 582 583 func TestMountIssue12141(t *testing.T) { 584 files := ` 585 -- hugo.toml -- 586 disableKinds = ["taxonomy", "term"] 587 [module] 588 [[module.mounts]] 589 source = "myfiles" 590 target = "static" 591 [[module.mounts]] 592 source = "myfiles/f1.txt" 593 target = "static/f2.txt" 594 -- myfiles/f1.txt -- 595 f1 596 ` 597 b := hugolib.Test(t, files) 598 fs := b.H.BaseFs.StaticFs("") 599 600 b.AssertFs(fs, ` 601 . true 602 f1.txt false 603 f2.txt false 604 `) 605 } 606 607 func checkFileCount(fs afero.Fs, dirname string, c *qt.C, expected int) { 608 c.Helper() 609 count, names, err := countFilesAndGetFilenames(fs, dirname) 610 namesComment := qt.Commentf("filenames: %v", names) 611 c.Assert(err, qt.IsNil, namesComment) 612 c.Assert(count, qt.Equals, expected, namesComment) 613 } 614 615 func checkFileContent(fs afero.Fs, filename string, c *qt.C, expected ...string) { 616 b, err := afero.ReadFile(fs, filename) 617 c.Assert(err, qt.IsNil) 618 619 content := string(b) 620 621 for _, e := range expected { 622 c.Assert(content, qt.Contains, e) 623 } 624 } 625 626 func countFilesAndGetFilenames(fs afero.Fs, dirname string) (int, []string, error) { 627 if fs == nil { 628 return 0, nil, errors.New("no fs") 629 } 630 631 counter := 0 632 var filenames []string 633 634 wf := func(path string, info hugofs.FileMetaInfo) error { 635 if !info.IsDir() { 636 counter++ 637 } 638 639 if info.Name() != "." { 640 name := info.Name() 641 name = strings.Replace(name, filepath.FromSlash("/my/work"), "WORK_DIR", 1) 642 filenames = append(filenames, name) 643 } 644 645 return nil 646 } 647 648 w := hugofs.NewWalkway(hugofs.WalkwayConfig{Fs: fs, Root: dirname, WalkFn: wf}) 649 650 if err := w.Walk(); err != nil { 651 return -1, nil, err 652 } 653 654 return counter, filenames, nil 655 } 656 657 func setConfigAndWriteSomeFilesTo(fs afero.Fs, v config.Provider, key, val string, num int) error { 658 workingDir := v.GetString("workingDir") 659 v.Set(key, val) 660 fs.Mkdir(val, 0o755) // nolint 661 for i := 0; i < num; i++ { 662 filename := filepath.Join(workingDir, val, fmt.Sprintf("f%d.txt", i+1)) 663 afero.WriteFile(fs, filename, []byte(fmt.Sprintf("content:%s:%d", key, i+1)), 0o755) // nolint 664 } 665 666 return nil 667 }