3434)
3535from ..requests import Requests
3636from ..url_metadata import RemoteUrlMetadata
37- from . import LockfileProvider , ModuleProvider , ProviderFactory , RCFileProvider
37+ from . import Config , ConfigProvider , LockfileProvider , ModuleProvider , ProviderFactory
3838from .special import SpecialSourceProvider
3939
4040_NPM_CORGIDOC = (
@@ -103,8 +103,103 @@ def process_lockfile(self, lockfile: Path) -> Iterator[Package]:
103103 yield from self .process_dependencies (lockfile , data .get ('dependencies' , {}))
104104
105105
106- class NpmRCFileProvider (RCFileProvider ):
107- RCFILE_NAME = '.npmrc'
106+ class NpmConfigProvider (ConfigProvider ):
107+ _COMMENT = ('#' , ';' )
108+
109+ @property
110+ def _filename (self ) -> str :
111+ return '.npmrc'
112+
113+ def _parse_value_as_json (self , string : str ) -> Any :
114+ try :
115+ return json .loads (string )
116+ except json .JSONDecodeError :
117+ return string
118+
119+ def _parse_value_literal (self , string : str ) -> str :
120+ result = ''
121+ escaped = False
122+ for c in string :
123+ if escaped :
124+ if c not in self ._COMMENT and c != '\\ ' :
125+ result += '\\ '
126+ result += c
127+ escaped = False
128+ elif c == '\\ ' :
129+ escaped = True
130+ elif c in self ._COMMENT :
131+ break
132+ else :
133+ result += c
134+
135+ if escaped :
136+ result += '\\ '
137+ return result .strip ()
138+
139+ def _parse_value (self , string : str ) -> Any :
140+ SINGLE_QUOTE = "'"
141+ DOUBLE_QUOTE = '"'
142+
143+ string = string .strip ()
144+
145+ if string .startswith (SINGLE_QUOTE ) and string .endswith (SINGLE_QUOTE ):
146+ return self ._parse_value_as_json (string [1 :- 1 ])
147+ elif string .startswith (DOUBLE_QUOTE ) and string .endswith (DOUBLE_QUOTE ):
148+ return self ._parse_value_as_json (string )
149+ else :
150+ return self ._parse_value_literal (string )
151+
152+ def _coalesce_to_string (self , value : Any ) -> Any :
153+ if isinstance (value , list ):
154+ return ',' .join (map (self ._coalesce_to_string , value ))
155+ elif isinstance (value , dict ):
156+ return '[object Object]'
157+ else :
158+ return str (value )
159+
160+ def parse_config (self , path : Path ) -> Dict [str , Any ]:
161+ LITERALS = {
162+ 'true' : True ,
163+ 'false' : False ,
164+ 'null' : None ,
165+ }
166+
167+ result : Dict [str , Any ] = {}
168+
169+ with path .open () as fp :
170+ for line in fp :
171+ line = line .strip ()
172+ if not line or line .startswith (self ._COMMENT ):
173+ continue
174+
175+ try :
176+ key_s , value_s = line .split ('=' , 1 )
177+ except ValueError :
178+ key_s = line
179+ value_s = 'true'
180+
181+ key = self ._coalesce_to_string (self ._parse_value (key_s ))
182+ is_array = False
183+ if key .endswith ('[]' ):
184+ is_array = True
185+ key = key [:- 2 ]
186+
187+ value = self ._parse_value (value_s )
188+ if isinstance (value , str ):
189+ value = LITERALS .get (value , value )
190+
191+ if is_array and key not in result :
192+ result [key ] = []
193+ elif is_array and not isinstance (result [key ], list ):
194+ result [key ] = [result [key ]]
195+
196+ previous_value = result .get (key )
197+ if isinstance (previous_value , list ):
198+ previous_value .append (value )
199+ else :
200+ result [key ] = value
201+
202+ return result
108203
109204
110205class NpmModuleProvider (ModuleProvider ):
@@ -124,13 +219,15 @@ def __init__(
124219 special : SpecialSourceProvider ,
125220 lockfile_root : Path ,
126221 options : Options ,
222+ lockfile_configs : Dict [Path , Config ],
127223 ) -> None :
128224 self .gen = gen
129225 self .special_source_provider = special
130226 self .lockfile_root = lockfile_root
131227 self .registry = options .registry
132228 self .no_autopatch = options .no_autopatch
133229 self .no_trim_index = options .no_trim_index
230+ self .lockfile_configs = lockfile_configs
134231 self .npm_cache_dir = self .gen .data_root / 'npm-cache'
135232 self .cacache_dir = self .npm_cache_dir / '_cacache'
136233 # Awaitable so multiple tasks can be waiting on the same package info.
@@ -143,8 +240,6 @@ def __init__(
143240 self .git_sources : DefaultDict [
144241 Path , Dict [Path , GitSource ]
145242 ] = collections .defaultdict (lambda : {})
146- # FIXME better pass the same provider object we created in main
147- self .rcfile_provider = NpmRCFileProvider ()
148243
149244 def __exit__ (
150245 self ,
@@ -324,21 +419,16 @@ async def generate_package(self, package: Package) -> None:
324419 def relative_lockfile_dir (self , lockfile : Path ) -> Path :
325420 return lockfile .parent .relative_to (self .lockfile_root )
326421
327- @functools .lru_cache (typed = True )
328- def get_lockfile_rc (self , lockfile : Path ) -> Dict [str , str ]:
329- rc = {}
330- rcfile_path = lockfile .parent / self .rcfile_provider .RCFILE_NAME
331- if rcfile_path .is_file ():
332- rc .update (self .rcfile_provider .parse_rcfile (rcfile_path ))
333- return rc
334-
335422 def get_package_registry (self , package : Package ) -> str :
336423 assert isinstance (package .source , RegistrySource )
337- rc = self .get_lockfile_rc (package .lockfile )
338- if rc and '/' in package .name :
339- scope , _ = package .name .split ('/' , maxsplit = 1 )
340- if f'{ scope } :registry' in rc :
341- return rc [f'{ scope } :registry' ]
424+ if '/' in package .name :
425+ config = self .lockfile_configs .get (package .lockfile )
426+ if config is not None :
427+ scope , _ = package .name .split ('/' , maxsplit = 1 )
428+ registry = config .get_registry_for_scope (scope )
429+ if registry is not None :
430+ return registry
431+
342432 return self .registry
343433
344434 def _finalize (self ) -> None :
@@ -468,10 +558,19 @@ def __init__(self, lockfile_root: Path, options: Options) -> None:
468558 def create_lockfile_provider (self ) -> NpmLockfileProvider :
469559 return NpmLockfileProvider (self .options .lockfile )
470560
471- def create_rcfile_providers (self ) -> List [ RCFileProvider ] :
472- return [ NpmRCFileProvider ()]
561+ def create_config_provider (self ) -> NpmConfigProvider :
562+ return NpmConfigProvider ()
473563
474564 def create_module_provider (
475- self , gen : ManifestGenerator , special : SpecialSourceProvider
565+ self ,
566+ gen : ManifestGenerator ,
567+ special : SpecialSourceProvider ,
568+ lockfile_configs : Dict [Path , Config ],
476569 ) -> NpmModuleProvider :
477- return NpmModuleProvider (gen , special , self .lockfile_root , self .options .module )
570+ return NpmModuleProvider (
571+ gen ,
572+ special ,
573+ self .lockfile_root ,
574+ self .options .module ,
575+ lockfile_configs ,
576+ )
0 commit comments