@@ -153,6 +153,54 @@ impl PackageManager {
153153 envs : HashMap :: from ( [ ( "PATH" . to_string ( ) , format_path_env ( self . get_bin_prefix ( ) ) ) ] ) ,
154154 }
155155 }
156+
157+ #[ must_use]
158+ pub fn get_fingerprint_ignores ( & self ) -> Vec < Str > {
159+ let mut ignores: Vec < Str > = vec ! [
160+ // ignore all files by default
161+ "**/*" . into( ) ,
162+ // keep all package.json files except under node_modules
163+ "!**/package.json" . into( ) ,
164+ "!**/.npmrc" . into( ) ,
165+ ] ;
166+ match self . client {
167+ PackageManagerType :: Pnpm => {
168+ ignores. push ( "!**/pnpm-workspace.yaml" . into ( ) ) ;
169+ ignores. push ( "!**/pnpm-lock.yaml" . into ( ) ) ;
170+ // https://pnpm.io/pnpmfile
171+ ignores. push ( "!**/.pnpmfile.cjs" . into ( ) ) ;
172+ ignores. push ( "!**/pnpmfile.cjs" . into ( ) ) ;
173+ // pnpm support Plug'n'Play https://pnpm.io/blog/2020/10/17/node-modules-configuration-options-with-pnpm#plugnplay-the-strictest-configuration
174+ ignores. push ( "!**/.pnp.cjs" . into ( ) ) ;
175+ }
176+ PackageManagerType :: Yarn => {
177+ ignores. push ( "!**/.yarnrc" . into ( ) ) ; // yarn 1.x
178+ ignores. push ( "!**/.yarnrc.yml" . into ( ) ) ; // yarn 2.x
179+ ignores. push ( "!**/yarn.config.cjs" . into ( ) ) ; // yarn 2.x
180+ ignores. push ( "!**/yarn.lock" . into ( ) ) ;
181+ // .yarn/patches, .yarn/releases
182+ ignores. push ( "!**/.yarn/**/*" . into ( ) ) ;
183+ // .pnp.cjs https://yarnpkg.com/features/pnp
184+ ignores. push ( "!**/.pnp.cjs" . into ( ) ) ;
185+ }
186+ PackageManagerType :: Npm => {
187+ ignores. push ( "!**/package-lock.json" . into ( ) ) ;
188+ ignores. push ( "!**/npm-shrinkwrap.json" . into ( ) ) ;
189+ }
190+ }
191+ // ignore all files under node_modules
192+ // e.g. node_modules/mqtt/package.json
193+ ignores. push ( "**/node_modules/**/*" . into ( ) ) ;
194+ // keep the node_modules directory
195+ ignores. push ( "!**/node_modules" . into ( ) ) ;
196+ // keep the scoped directory
197+ ignores. push ( "!**/node_modules/@*" . into ( ) ) ;
198+ // ignore all patterns under nested node_modules
199+ // e.g. node_modules/mqtt/node_modules/mqtt-packet/node_modules
200+ ignores. push ( "**/node_modules/**/node_modules/**" . into ( ) ) ;
201+
202+ ignores
203+ }
156204}
157205
158206/// The package root directory and its package.json file.
@@ -328,11 +376,17 @@ fn get_package_manager_type_and_version(
328376 return Ok ( ( PackageManagerType :: Npm , version, None ) ) ;
329377 }
330378
331- // if pnpmfile.cjs exists, use pnpm@latest
332- let pnpmfile_cjs_path = workspace_root. path . join ( "pnpmfile.cjs" ) ;
379+ // if . pnpmfile.cjs exists, use pnpm@latest
380+ let pnpmfile_cjs_path = workspace_root. path . join ( ". pnpmfile.cjs" ) ;
333381 if is_exists_file ( & pnpmfile_cjs_path) ? {
334382 return Ok ( ( PackageManagerType :: Pnpm , version, None ) ) ;
335383 }
384+ // if legacy pnpmfile.cjs exists, use pnpm@latest
385+ // https://newreleases.io/project/npm/pnpm/release/6.0.0
386+ let legacy_pnpmfile_cjs_path = workspace_root. path . join ( "pnpmfile.cjs" ) ;
387+ if is_exists_file ( & legacy_pnpmfile_cjs_path) ? {
388+ return Ok ( ( PackageManagerType :: Pnpm , version, None ) ) ;
389+ }
336390
337391 // if yarn.config.cjs exists, use yarn@latest (yarn 2.0+)
338392 let yarn_config_cjs_path = workspace_root. path . join ( "yarn.config.cjs" ) ;
@@ -1611,4 +1665,232 @@ mod tests {
16111665 "pnpmfile.cjs should be detected before yarn.config.cjs"
16121666 ) ;
16131667 }
1668+
1669+ // Tests for get_fingerprint_ignores method
1670+ mod get_fingerprint_ignores_tests {
1671+ use vite_glob:: GlobPatternSet ;
1672+
1673+ use super :: * ;
1674+
1675+ fn create_mock_package_manager ( pm_type : PackageManagerType ) -> PackageManager {
1676+ let temp_dir = create_temp_dir ( ) ;
1677+ let temp_dir_path = AbsolutePathBuf :: new ( temp_dir. path ( ) . to_path_buf ( ) ) . unwrap ( ) ;
1678+ let install_dir = temp_dir_path. join ( "install" ) ;
1679+
1680+ PackageManager {
1681+ client : pm_type,
1682+ package_name : pm_type. to_string ( ) . into ( ) ,
1683+ version : "1.0.0" . into ( ) ,
1684+ hash : None ,
1685+ bin_name : pm_type. to_string ( ) . into ( ) ,
1686+ workspace_root : temp_dir_path. clone ( ) ,
1687+ install_dir,
1688+ }
1689+ }
1690+
1691+ #[ test]
1692+ fn test_pnpm_fingerprint_ignores ( ) {
1693+ let pm = create_mock_package_manager ( PackageManagerType :: Pnpm ) ;
1694+ let ignores = pm. get_fingerprint_ignores ( ) ;
1695+ let matcher = GlobPatternSet :: new ( & ignores) . expect ( "Should compile patterns" ) ;
1696+
1697+ // Should ignore most files in node_modules
1698+ assert ! (
1699+ matcher. is_match( "node_modules/pkg-a/index.js" ) ,
1700+ "Should ignore implementation files"
1701+ ) ;
1702+ assert ! (
1703+ matcher. is_match( "foo/bar/node_modules/pkg-a/lib/util.js" ) ,
1704+ "Should ignore nested files"
1705+ ) ;
1706+ assert ! ( matcher. is_match( "node_modules/.bin/cli" ) , "Should ignore binaries" ) ;
1707+
1708+ // Should NOT ignore package.json files (including in node_modules)
1709+ assert ! ( !matcher. is_match( "package.json" ) , "Should NOT ignore root package.json" ) ;
1710+ assert ! (
1711+ !matcher. is_match( "packages/app/package.json" ) ,
1712+ "Should NOT ignore package package.json"
1713+ ) ;
1714+
1715+ // Should ignore package.json files under node_modules
1716+ assert ! (
1717+ matcher. is_match( "node_modules/pkg-a/package.json" ) ,
1718+ "Should ignore package.json in node_modules"
1719+ ) ;
1720+ assert ! (
1721+ matcher. is_match( "foo/bar/node_modules/pkg-a/package.json" ) ,
1722+ "Should ignore package.json in node_modules"
1723+ ) ;
1724+ assert ! (
1725+ matcher. is_match( "node_modules/@scope/pkg-a/package.json" ) ,
1726+ "Should ignore package.json in node_modules"
1727+ ) ;
1728+
1729+ // Should keep node_modules directories themselves
1730+ assert ! ( !matcher. is_match( "node_modules" ) , "Should NOT ignore node_modules directory" ) ;
1731+ assert ! (
1732+ !matcher. is_match( "packages/app/node_modules" ) ,
1733+ "Should NOT ignore nested node_modules"
1734+ ) ;
1735+ assert ! (
1736+ matcher. is_match( "node_modules/mqtt/node_modules" ) ,
1737+ "Should ignore sub node_modules under node_modules"
1738+ ) ;
1739+ assert ! (
1740+ matcher
1741+ . is_match( "node_modules/minimatch/node_modules/brace-expansion/node_modules" ) ,
1742+ "Should ignore sub node_modules under node_modules"
1743+ ) ;
1744+ assert ! (
1745+ matcher. is_match( "packages/app/node_modules/@octokit/graphql/node_modules" ) ,
1746+ "Should ignore sub node_modules under node_modules"
1747+ ) ;
1748+
1749+ // Should keep the root scoped directory under node_modules
1750+ assert ! ( !matcher. is_match( "node_modules/@types" ) , "Should NOT ignore scoped directory" ) ;
1751+ assert ! (
1752+ matcher. is_match( "node_modules/@types/node" ) ,
1753+ "Should ignore scoped sub directory"
1754+ ) ;
1755+
1756+ // Pnpm-specific files should NOT be ignored
1757+ assert ! (
1758+ !matcher. is_match( "pnpm-workspace.yaml" ) ,
1759+ "Should NOT ignore pnpm-workspace.yaml"
1760+ ) ;
1761+ assert ! ( !matcher. is_match( "pnpm-lock.yaml" ) , "Should NOT ignore pnpm-lock.yaml" ) ;
1762+ assert ! ( !matcher. is_match( ".pnpmfile.cjs" ) , "Should NOT ignore .pnpmfile.cjs" ) ;
1763+ assert ! ( !matcher. is_match( "pnpmfile.cjs" ) , "Should NOT ignore pnpmfile.cjs" ) ;
1764+ assert ! ( !matcher. is_match( ".pnp.cjs" ) , "Should NOT ignore .pnp.cjs" ) ;
1765+ assert ! ( !matcher. is_match( ".npmrc" ) , "Should NOT ignore .npmrc" ) ;
1766+
1767+ // Other package manager files should be ignored
1768+ assert ! ( matcher. is_match( "yarn.lock" ) , "Should ignore yarn.lock" ) ;
1769+ assert ! ( matcher. is_match( "package-lock.json" ) , "Should ignore package-lock.json" ) ;
1770+
1771+ // Regular source files should be ignored
1772+ assert ! ( matcher. is_match( "src/index.js" ) , "Should ignore source files" ) ;
1773+ assert ! ( matcher. is_match( "dist/bundle.js" ) , "Should ignore build outputs" ) ;
1774+ }
1775+
1776+ #[ test]
1777+ fn test_yarn_fingerprint_ignores ( ) {
1778+ let pm = create_mock_package_manager ( PackageManagerType :: Yarn ) ;
1779+ let ignores = pm. get_fingerprint_ignores ( ) ;
1780+ let matcher = GlobPatternSet :: new ( & ignores) . expect ( "Should compile patterns" ) ;
1781+
1782+ // Should ignore most files in node_modules
1783+ assert ! (
1784+ matcher. is_match( "node_modules/react/index.js" ) ,
1785+ "Should ignore implementation files"
1786+ ) ;
1787+ assert ! (
1788+ matcher. is_match( "node_modules/react/cjs/react.production.js" ) ,
1789+ "Should ignore nested files"
1790+ ) ;
1791+
1792+ // Should NOT ignore package.json files (including in node_modules)
1793+ assert ! ( !matcher. is_match( "package.json" ) , "Should NOT ignore root package.json" ) ;
1794+ assert ! (
1795+ !matcher. is_match( "apps/web/package.json" ) ,
1796+ "Should NOT ignore app package.json"
1797+ ) ;
1798+
1799+ // Should ignore package.json files under node_modules
1800+ assert ! (
1801+ matcher. is_match( "node_modules/react/package.json" ) ,
1802+ "Should ignore package.json in node_modules"
1803+ ) ;
1804+
1805+ // Should keep node_modules directories
1806+ assert ! ( !matcher. is_match( "node_modules" ) , "Should NOT ignore node_modules directory" ) ;
1807+ assert ! ( !matcher. is_match( "node_modules/@types" ) , "Should NOT ignore scoped packages" ) ;
1808+
1809+ // Yarn-specific files should NOT be ignored
1810+ assert ! ( !matcher. is_match( ".yarnrc" ) , "Should NOT ignore .yarnrc" ) ;
1811+ assert ! ( !matcher. is_match( ".yarnrc.yml" ) , "Should NOT ignore .yarnrc.yml" ) ;
1812+ assert ! ( !matcher. is_match( "yarn.config.cjs" ) , "Should NOT ignore yarn.config.cjs" ) ;
1813+ assert ! ( !matcher. is_match( "yarn.lock" ) , "Should NOT ignore yarn.lock" ) ;
1814+ assert ! (
1815+ !matcher. is_match( ".yarn/releases/yarn-4.0.0.cjs" ) ,
1816+ "Should NOT ignore .yarn contents"
1817+ ) ;
1818+ assert ! (
1819+ !matcher. is_match( ".yarn/patches/package.patch" ) ,
1820+ "Should NOT ignore .yarn patches"
1821+ ) ;
1822+ assert ! (
1823+ !matcher. is_match( ".yarn/patches/yjs-npm-13.6.21-c9f1f3397c.patch" ) ,
1824+ "Should NOT ignore .yarn patches"
1825+ ) ;
1826+ assert ! ( !matcher. is_match( ".pnp.cjs" ) , "Should NOT ignore .pnp.cjs" ) ;
1827+ assert ! ( !matcher. is_match( ".npmrc" ) , "Should NOT ignore .npmrc" ) ;
1828+
1829+ // Other package manager files should be ignored
1830+ assert ! ( matcher. is_match( "pnpm-lock.yaml" ) , "Should ignore pnpm-lock.yaml" ) ;
1831+ assert ! ( matcher. is_match( "package-lock.json" ) , "Should ignore package-lock.json" ) ;
1832+
1833+ // Regular source files should be ignored
1834+ assert ! ( matcher. is_match( "src/components/Button.tsx" ) , "Should ignore source files" ) ;
1835+
1836+ // Should ignore nested node_modules
1837+ assert ! (
1838+ matcher. is_match(
1839+ "node_modules/@mixmark-io/domino/.yarn/plugins/@yarnpkg/plugin-version.cjs"
1840+ ) ,
1841+ "Should ignore sub node_modules under node_modules"
1842+ ) ;
1843+ assert ! (
1844+ matcher. is_match( "node_modules/touch/node_modules" ) ,
1845+ "Should ignore sub node_modules under node_modules"
1846+ ) ;
1847+ }
1848+
1849+ #[ test]
1850+ fn test_npm_fingerprint_ignores ( ) {
1851+ let pm = create_mock_package_manager ( PackageManagerType :: Npm ) ;
1852+ let ignores = pm. get_fingerprint_ignores ( ) ;
1853+ let matcher = GlobPatternSet :: new ( & ignores) . expect ( "Should compile patterns" ) ;
1854+
1855+ // Should ignore most files in node_modules
1856+ assert ! (
1857+ matcher. is_match( "node_modules/express/index.js" ) ,
1858+ "Should ignore implementation files"
1859+ ) ;
1860+ assert ! (
1861+ matcher. is_match( "node_modules/express/lib/application.js" ) ,
1862+ "Should ignore nested files"
1863+ ) ;
1864+
1865+ // Should NOT ignore package.json files (including in node_modules)
1866+ assert ! ( !matcher. is_match( "package.json" ) , "Should NOT ignore root package.json" ) ;
1867+ assert ! ( !matcher. is_match( "src/package.json" ) , "Should NOT ignore nested package.json" ) ;
1868+
1869+ // Should ignore package.json files under node_modules
1870+ assert ! (
1871+ matcher. is_match( "node_modules/express/package.json" ) ,
1872+ "Should ignore package.json in node_modules"
1873+ ) ;
1874+
1875+ // Should keep node_modules directories
1876+ assert ! ( !matcher. is_match( "node_modules" ) , "Should NOT ignore node_modules directory" ) ;
1877+ assert ! ( !matcher. is_match( "node_modules/@babel" ) , "Should NOT ignore scoped packages" ) ;
1878+
1879+ // Npm-specific files should NOT be ignored
1880+ assert ! ( !matcher. is_match( "package-lock.json" ) , "Should NOT ignore package-lock.json" ) ;
1881+ assert ! (
1882+ !matcher. is_match( "npm-shrinkwrap.json" ) ,
1883+ "Should NOT ignore npm-shrinkwrap.json"
1884+ ) ;
1885+ assert ! ( !matcher. is_match( ".npmrc" ) , "Should NOT ignore .npmrc" ) ;
1886+
1887+ // Other package manager files should be ignored
1888+ assert ! ( matcher. is_match( "pnpm-lock.yaml" ) , "Should ignore pnpm-lock.yaml" ) ;
1889+ assert ! ( matcher. is_match( "yarn.lock" ) , "Should ignore yarn.lock" ) ;
1890+
1891+ // Regular files should be ignored
1892+ assert ! ( matcher. is_match( "README.md" ) , "Should ignore docs" ) ;
1893+ assert ! ( matcher. is_match( "src/app.ts" ) , "Should ignore source files" ) ;
1894+ }
1895+ }
16141896}
0 commit comments