Coverage for lintro / tools / core / install_strategies / npm_strategy.py: 94%

34 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-04-03 18:53 +0000

1"""npm/bun install strategy.""" 

2 

3from __future__ import annotations 

4 

5from lintro.enums.install_context import InstallContext, PackageManager 

6from lintro.tools.core.install_strategies.base import InstallStrategy 

7from lintro.tools.core.install_strategies.brew_names import BREW_FORMULA_NAMES 

8from lintro.tools.core.install_strategies.environment import InstallEnvironment 

9from lintro.tools.core.install_strategies.registry import register_strategy 

10 

11 

12class NpmStrategy(InstallStrategy): 

13 """Install strategy for npm/bun-managed JavaScript packages.""" 

14 

15 def install_type(self) -> str: 

16 """Return ``'npm'``.""" 

17 return "npm" 

18 

19 def is_available(self, env: InstallEnvironment) -> bool: 

20 """Available if bun, npm, or brew exists.""" 

21 return ( 

22 env.has(PackageManager.BUN) 

23 or env.has(PackageManager.NPM) 

24 or env.has(PackageManager.BREW) 

25 ) 

26 

27 def check_prerequisites( 

28 self, 

29 env: InstallEnvironment, 

30 tool_name: str, 

31 ) -> str | None: 

32 """Return skip reason if no JS package manager is available. 

33 

34 Args: 

35 env: The current install environment. 

36 tool_name: Canonical tool name. 

37 

38 Returns: 

39 Skip reason or None. 

40 """ 

41 if env.has(PackageManager.BUN) or env.has(PackageManager.NPM): 

42 return None 

43 if env.has(PackageManager.BREW) and tool_name in BREW_FORMULA_NAMES: 

44 return None 

45 return "bun/npm not available (install Node.js first)" 

46 

47 def install_hint( 

48 self, 

49 env: InstallEnvironment, 

50 tool_name: str, 

51 tool_version: str, 

52 install_package: str | None, 

53 _install_component: str | None, 

54 ) -> str: 

55 """Generate npm install hint. 

56 

57 Args: 

58 env: The current install environment. 

59 tool_name: Canonical tool name. 

60 tool_version: Expected version. 

61 install_package: Package name override. 

62 _install_component: Unused for npm. 

63 

64 Returns: 

65 Shell command string. 

66 """ 

67 pkg = install_package or tool_name 

68 brew_pkg = BREW_FORMULA_NAMES.get(tool_name) 

69 if ( 

70 brew_pkg 

71 and env.has(PackageManager.BREW) 

72 and ( 

73 _is_homebrew_context(env) 

74 or not (env.has(PackageManager.BUN) or env.has(PackageManager.NPM)) 

75 ) 

76 ): 

77 return f"brew install {brew_pkg}" 

78 return f"{_npm_cmd(env)} {pkg}@{tool_version}" 

79 

80 def upgrade_hint( 

81 self, 

82 env: InstallEnvironment, 

83 tool_name: str, 

84 tool_version: str, 

85 install_package: str | None, 

86 _install_component: str | None, 

87 ) -> str: 

88 """Generate npm upgrade hint (npm replaces on install). 

89 

90 Args: 

91 env: The current install environment. 

92 tool_name: Canonical tool name. 

93 tool_version: Expected version. 

94 install_package: Package name override. 

95 _install_component: Unused for npm. 

96 

97 Returns: 

98 Shell command string. 

99 """ 

100 pkg = install_package or tool_name 

101 brew_pkg = BREW_FORMULA_NAMES.get(tool_name) 

102 if ( 

103 brew_pkg 

104 and env.has(PackageManager.BREW) 

105 and ( 

106 _is_homebrew_context(env) 

107 or not (env.has(PackageManager.BUN) or env.has(PackageManager.NPM)) 

108 ) 

109 ): 

110 return f"brew upgrade {brew_pkg}" 

111 return f"{_npm_cmd(env)} {pkg}@{tool_version}" 

112 

113 

114def _npm_cmd(env: InstallEnvironment) -> str: 

115 """Return the preferred npm install command prefix.""" 

116 return "bun add -g" if env.has(PackageManager.BUN) else "npm install -g" 

117 

118 

119def _is_homebrew_context(env: InstallEnvironment) -> bool: 

120 """Check if the environment is a Homebrew install with brew available.""" 

121 return env.has(PackageManager.BREW) and env.install_context in ( 

122 InstallContext.HOMEBREW_FULL, 

123 InstallContext.HOMEBREW_BIN, 

124 ) 

125 

126 

127register_strategy(NpmStrategy())