1
1
#[ cfg( test) ]
2
2
mod scanner {
3
- use std:: path:: PathBuf ;
3
+ use std:: path:: { Path , PathBuf } ;
4
4
use std:: process:: Command ;
5
5
use std:: thread:: sleep;
6
6
use std:: time:: Duration ;
@@ -9,6 +9,16 @@ mod scanner {
9
9
use tailwindcss_oxide:: * ;
10
10
use tempfile:: tempdir;
11
11
12
+ fn symlink < P : AsRef < Path > , Q : AsRef < Path > > ( original : P , link : Q ) -> std:: io:: Result < ( ) > {
13
+ #[ cfg( not( windows) ) ]
14
+ let result = std:: os:: unix:: fs:: symlink ( original, link) ;
15
+
16
+ #[ cfg( windows) ]
17
+ let result = std:: os:: windows:: fs:: symlink_dir ( original, link) ;
18
+
19
+ result
20
+ }
21
+
12
22
fn public_source_entry_from_pattern ( dir : PathBuf , pattern : & str ) -> PublicSourceEntry {
13
23
let mut parts = pattern. split_whitespace ( ) ;
14
24
let _ = parts. next ( ) . unwrap_or_default ( ) ;
@@ -646,8 +656,8 @@ mod scanner {
646
656
assert_eq ! (
647
657
candidates,
648
658
vec![
649
- "content-['project-a/index.html']" . to_owned ( ) ,
650
- "content-['project-b/index.html']" . to_owned ( ) ,
659
+ "content-['project-a/index.html']" ,
660
+ "content-['project-b/index.html']"
651
661
]
652
662
) ;
653
663
}
@@ -702,8 +712,8 @@ mod scanner {
702
712
assert_eq ! (
703
713
candidates,
704
714
vec![
705
- "content-['project-a/index.html']" . to_owned ( ) ,
706
- "content-['project-b/index.html']" . to_owned ( ) ,
715
+ "content-['project-a/index.html']" ,
716
+ "content-['project-b/index.html']"
707
717
]
708
718
) ;
709
719
@@ -726,10 +736,10 @@ mod scanner {
726
736
assert_eq ! (
727
737
candidates,
728
738
vec![
729
- "content-['project-a/index.html']" . to_owned ( ) ,
730
- "content-['project-a/new.html']" . to_owned ( ) ,
731
- "content-['project-b/index.html']" . to_owned ( ) ,
732
- "content-['project-b/new.html']" . to_owned ( ) ,
739
+ "content-['project-a/index.html']" ,
740
+ "content-['project-a/new.html']" ,
741
+ "content-['project-b/index.html']" ,
742
+ "content-['project-b/new.html']"
733
743
]
734
744
) ;
735
745
@@ -758,12 +768,12 @@ mod scanner {
758
768
assert_eq ! (
759
769
candidates,
760
770
vec![
761
- "content-['project-a/index.html']" . to_owned ( ) ,
762
- "content-['project-a/new.html']" . to_owned ( ) ,
763
- "content-['project-a/sub1/sub2/index.html']" . to_owned ( ) ,
764
- "content-['project-b/index.html']" . to_owned ( ) ,
765
- "content-['project-b/new.html']" . to_owned ( ) ,
766
- "content-['project-b/sub1/sub2/index.html']" . to_owned ( ) ,
771
+ "content-['project-a/index.html']" ,
772
+ "content-['project-a/new.html']" ,
773
+ "content-['project-a/sub1/sub2/index.html']" ,
774
+ "content-['project-b/index.html']" ,
775
+ "content-['project-b/new.html']" ,
776
+ "content-['project-b/sub1/sub2/index.html']"
767
777
]
768
778
) ;
769
779
@@ -792,14 +802,14 @@ mod scanner {
792
802
assert_eq ! (
793
803
candidates,
794
804
vec![
795
- "content-['project-a/index.html']" . to_owned ( ) ,
796
- "content-['project-a/new.html']" . to_owned ( ) ,
797
- "content-['project-a/sub1/sub2/index.html']" . to_owned ( ) ,
798
- "content-['project-a/sub1/sub2/new.html']" . to_owned ( ) ,
799
- "content-['project-b/index.html']" . to_owned ( ) ,
800
- "content-['project-b/new.html']" . to_owned ( ) ,
801
- "content-['project-b/sub1/sub2/index.html']" . to_owned ( ) ,
802
- "content-['project-b/sub1/sub2/new.html']" . to_owned ( ) ,
805
+ "content-['project-a/index.html']" ,
806
+ "content-['project-a/new.html']" ,
807
+ "content-['project-a/sub1/sub2/index.html']" ,
808
+ "content-['project-a/sub1/sub2/new.html']" ,
809
+ "content-['project-b/index.html']" ,
810
+ "content-['project-b/new.html']" ,
811
+ "content-['project-b/sub1/sub2/index.html']" ,
812
+ "content-['project-b/sub1/sub2/new.html']"
803
813
]
804
814
) ;
805
815
}
@@ -1611,4 +1621,117 @@ mod scanner {
1611
1621
assert_eq ! ( globs, vec![ "*" , "src/*/*.{aspx,astro,cjs,cts,eex,erb,gjs,gts,haml,handlebars,hbs,heex,html,jade,js,jsx,liquid,md,mdx,mjs,mts,mustache,njk,nunjucks,php,pug,py,razor,rb,rhtml,rs,slim,svelte,tpl,ts,tsx,twig,vue}" ] ) ;
1612
1622
assert_eq ! ( normalized_sources, vec![ "**/*" ] ) ;
1613
1623
}
1624
+
1625
+ #[ test]
1626
+ fn test_glob_with_symlinks ( ) {
1627
+ let dir = tempdir ( ) . unwrap ( ) . into_path ( ) ;
1628
+ create_files_in (
1629
+ & dir,
1630
+ & [
1631
+ ( ".gitignore" , "node_modules\n dist" ) ,
1632
+ (
1633
+ "node_modules/.pnpm/@org+my-ui-library/dist/index.ts" ,
1634
+ "content-['node_modules/.pnpm/@org+my-ui-library/dist/index.ts']" ,
1635
+ ) ,
1636
+ // Make sure the `@org` does exist
1637
+ ( "node_modules/@org/.gitkeep" , "" ) ,
1638
+ ] ,
1639
+ ) ;
1640
+ let _ = symlink (
1641
+ dir. join ( "node_modules/.pnpm/@org+my-ui-library" ) ,
1642
+ dir. join ( "node_modules/@org/my-ui-library" ) ,
1643
+ ) ;
1644
+
1645
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1646
+ dir. clone( ) ,
1647
+ "@source 'node_modules'" ,
1648
+ ) ] ) ;
1649
+ let candidates = scanner. scan ( ) ;
1650
+
1651
+ assert_eq ! (
1652
+ candidates,
1653
+ vec![ "content-['node_modules/.pnpm/@org+my-ui-library/dist/index.ts']" ]
1654
+ ) ;
1655
+
1656
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1657
+ dir. clone( ) ,
1658
+ "@source 'node_modules/@org/my-ui-library'" ,
1659
+ ) ] ) ;
1660
+ let candidates = scanner. scan ( ) ;
1661
+
1662
+ assert_eq ! (
1663
+ candidates,
1664
+ vec![ "content-['node_modules/.pnpm/@org+my-ui-library/dist/index.ts']" ]
1665
+ ) ;
1666
+
1667
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1668
+ dir. clone( ) ,
1669
+ "@source 'node_modules/@org'" ,
1670
+ ) ] ) ;
1671
+ let candidates = scanner. scan ( ) ;
1672
+
1673
+ assert_eq ! (
1674
+ candidates,
1675
+ vec![ "content-['node_modules/.pnpm/@org+my-ui-library/dist/index.ts']" ]
1676
+ ) ;
1677
+ }
1678
+
1679
+ #[ test]
1680
+ fn test_globs_with_recursive_symlinks ( ) {
1681
+ let dir = tempdir ( ) . unwrap ( ) . into_path ( ) ;
1682
+ create_files_in (
1683
+ & dir,
1684
+ & [
1685
+ ( "b/index.html" , "content-['b/index.html']" ) ,
1686
+ ( "z/index.html" , "content-['z/index.html']" ) ,
1687
+ ] ,
1688
+ ) ;
1689
+
1690
+ // Create recursive symlinks
1691
+ let _ = symlink ( dir. join ( "a" ) , dir. join ( "b" ) ) ;
1692
+ let _ = symlink ( dir. join ( "b/c" ) , dir. join ( "c" ) ) ;
1693
+ let _ = symlink ( dir. join ( "b/root" ) , & dir) ;
1694
+ let _ = symlink ( dir. join ( "c" ) , dir. join ( "a" ) ) ;
1695
+
1696
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1697
+ dir. clone( ) ,
1698
+ "@source '.'" ,
1699
+ ) ] ) ;
1700
+ let candidates = scanner. scan ( ) ;
1701
+
1702
+ assert_eq ! (
1703
+ candidates,
1704
+ vec![ "content-['b/index.html']" , "content-['z/index.html']" ]
1705
+ ) ;
1706
+ }
1707
+
1708
+ #[ test]
1709
+ fn test_partial_globs_with_symlinks ( ) {
1710
+ let dir = tempdir ( ) . unwrap ( ) . into_path ( ) ;
1711
+ create_files_in ( & dir, & [ ( "abcd/xyz.html" , "content-['abcd/xyz.html']" ) ] ) ;
1712
+ let _ = symlink ( dir. join ( "abcd" ) , dir. join ( "efgh" ) ) ;
1713
+
1714
+ // No sources should find nothing
1715
+ let mut scanner = Scanner :: new ( vec ! [ ] ) ;
1716
+ let candidates = scanner. scan ( ) ;
1717
+ assert ! ( candidates. is_empty( ) ) ;
1718
+
1719
+ // Full symlinked folder name, should find the file
1720
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1721
+ dir. clone( ) ,
1722
+ "@source 'efgh/*.html'" ,
1723
+ ) ] ) ;
1724
+ let candidates = scanner. scan ( ) ;
1725
+
1726
+ assert_eq ! ( candidates, vec![ "content-['abcd/xyz.html']" ] ) ;
1727
+
1728
+ // Partially referencing the symlinked folder with a glob, should find the file
1729
+ let mut scanner = Scanner :: new ( vec ! [ public_source_entry_from_pattern(
1730
+ dir. clone( ) ,
1731
+ "@source 'ef*/*.html'" ,
1732
+ ) ] ) ;
1733
+ let candidates = scanner. scan ( ) ;
1734
+
1735
+ assert_eq ! ( candidates, vec![ "content-['abcd/xyz.html']" ] ) ;
1736
+ }
1614
1737
}
0 commit comments