4141 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
4242)
4343
44+ # TODO Once lockfile v2 syntax support is complete, use _process_packages_v2 for
45+ # both v2 and v3 lockfiles
46+ _LOCKFILE_V1_VERSIONS = {1 , 2 }
47+ _LOCKFILE_V2_VERSIONS = {3 }
48+
4449
4550class NpmLockfileProvider (LockfileProvider ):
4651 _ALIAS_RE = re .compile (r'^npm:(.[^@]*)@(.*)$' )
@@ -106,6 +111,16 @@ def _process_packages_v2(
106111 continue
107112
108113 name = info .get ('name' )
114+ inferred_name = False
115+
116+ # NOTE We can't reliably determine the package name from the lockfile v2 syntax,
117+ # but we need it for registry queries and special source processing;
118+ # If we didn't get the package name at this point, try determining it from
119+ # the install path as the last resort
120+ if name is None :
121+ inferred_name = True
122+ path_list = install_path .split ('/' )
123+ name = '/' .join (path_list [- path_list [::- 1 ].index ('node_modules' ) :])
109124
110125 source : PackageSource
111126 package_json_path = lockfile .parent / install_path / 'package.json'
@@ -114,7 +129,8 @@ def _process_packages_v2(
114129 and package_json_path .exists ()
115130 ):
116131 source = LocalSource (path = install_path )
117- if name is None :
132+ if inferred_name :
133+ # Prefer getting the name directly instead of inferring it.
118134 with package_json_path .open ('rb' ) as fp :
119135 name = json .load (fp )['name' ]
120136 elif 'resolved' in info :
@@ -130,23 +146,12 @@ def _process_packages_v2(
130146 integrity = integrity , resolved = info ['resolved' ]
131147 )
132148 elif resolved_url .scheme .startswith ('git+' ):
133- raise NotImplementedError (
134- 'Git sources in lockfile v2 format are not supported yet'
135- f' (package { install_path } in { lockfile } )'
136- )
149+ source = self .parse_git_source (info ['resolved' ], name )
137150 else :
138151 raise NotImplementedError (
139152 f"Don't know how to handle package { install_path } in { lockfile } "
140153 )
141154
142- # NOTE We can't reliably determine the package name from the lockfile v2 syntax,
143- # but we need it for registry queries and special source processing;
144- # If we didn't get the package name at this point, try determining it from
145- # the install path as the last resort
146- if name is None :
147- path_list = install_path .split ('/' )
148- name = '/' .join (path_list [- path_list [::- 1 ].index ('node_modules' ) :])
149-
150155 yield Package (
151156 name = name ,
152157 version = info .get ('version' ),
@@ -158,11 +163,9 @@ def process_lockfile(self, lockfile: Path) -> Iterator[Package]:
158163 with open (lockfile ) as fp :
159164 data = json .load (fp )
160165
161- # TODO Once lockfile v2 syntax support is complete, use _process_packages_v2
162- # for both v2 and v2 lockfiles
163- if data ['lockfileVersion' ] in {1 , 2 }:
166+ if data ['lockfileVersion' ] in _LOCKFILE_V1_VERSIONS :
164167 yield from self ._process_packages_v1 (lockfile , data )
165- elif data ['lockfileVersion' ] in { 3 } :
168+ elif data ['lockfileVersion' ] in _LOCKFILE_V2_VERSIONS :
166169 yield from self ._process_packages_v2 (lockfile , data )
167170 else :
168171 raise NotImplementedError (
@@ -426,14 +429,21 @@ def _finalize(self) -> None:
426429
427430 if self .git_sources :
428431 # Generate jq scripts to patch the package*.json files.
432+
433+ # NOTE: In package.json, we always assign to .value regardless of
434+ # $match_against, because even if we're matching against the keys / package
435+ # names, what we actually want to set is the value / version. With
436+ # package-lock.json, we are in fact matching the values themselves,
437+ # so we re-assign to it directly.
429438 scripts = {
430439 'package.json' : r"""
431440 walk(
432441 if type == "object"
433442 then
434443 to_entries | map(
435- if (.value | type == "string") and $data[.value]
436- then .value = "git+file:\($buildroot)/\($data[.value])"
444+ .[$match_against] as $match |
445+ if ($match | type == "string") and $data[$match]
446+ then .value = "git+file:\($buildroot)/\($data[$match])"
437447 else .
438448 end
439449 ) | from_entries
@@ -443,16 +453,34 @@ def _finalize(self) -> None:
443453 """ ,
444454 'package-lock.json' : r"""
445455 walk(
446- if type == "object" and (.version | type == "string") and $data[.version]
447- then
448- .version = "git+file:\($buildroot)/\($data[.version])"
456+ if type == "object" then
457+ .[$match_against] as $match |
458+ if ($match | type == "string") and $data[$match]
459+ then .[$match_against] =
460+ "git+file:\($buildroot)/\($data[$match])"
461+ else .
462+ end
449463 else .
450464 end
451465 )
452466 """ ,
453467 }
454468
455469 for lockfile , sources in self .git_sources .items ():
470+ with lockfile .open () as fp :
471+ version = json .load (fp )['lockfileVersion' ]
472+
473+ if version in _LOCKFILE_V2_VERSIONS :
474+ match_against = {
475+ 'package.json' : 'key' ,
476+ 'package-lock.json' : 'resolved' ,
477+ }
478+ else :
479+ match_against = {
480+ 'package.json' : 'value' ,
481+ 'package-lock.json' : 'version' ,
482+ }
483+
456484 prefix = self .relative_lockfile_dir (lockfile )
457485 data : Dict [str , Dict [str , str ]] = {
458486 'package.json' : {},
@@ -486,6 +514,7 @@ def _finalize(self) -> None:
486514 patch_commands [lockfile ].append (
487515 'jq'
488516 ' --arg buildroot "$FLATPAK_BUILDER_BUILDDIR"'
517+ f' --arg match_against { match_against [filename ]} '
489518 f' --argjson data { shlex .quote (json_data )} '
490519 f' { shlex .quote (script )} { target } '
491520 f' > { target } .new'
0 commit comments