41
41
'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*'
42
42
)
43
43
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
+
44
49
45
50
class NpmLockfileProvider (LockfileProvider ):
46
51
_ALIAS_RE = re .compile (r'^npm:(.[^@]*)@(.*)$' )
@@ -106,6 +111,16 @@ def _process_packages_v2(
106
111
continue
107
112
108
113
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' ) :])
109
124
110
125
source : PackageSource
111
126
package_json_path = lockfile .parent / install_path / 'package.json'
@@ -114,7 +129,8 @@ def _process_packages_v2(
114
129
and package_json_path .exists ()
115
130
):
116
131
source = LocalSource (path = install_path )
117
- if name is None :
132
+ if inferred_name :
133
+ # Prefer getting the name directly instead of inferring it.
118
134
with package_json_path .open ('rb' ) as fp :
119
135
name = json .load (fp )['name' ]
120
136
elif 'resolved' in info :
@@ -130,23 +146,12 @@ def _process_packages_v2(
130
146
integrity = integrity , resolved = info ['resolved' ]
131
147
)
132
148
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 )
137
150
else :
138
151
raise NotImplementedError (
139
152
f"Don't know how to handle package { install_path } in { lockfile } "
140
153
)
141
154
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
-
150
155
yield Package (
151
156
name = name ,
152
157
version = info .get ('version' ),
@@ -158,11 +163,9 @@ def process_lockfile(self, lockfile: Path) -> Iterator[Package]:
158
163
with open (lockfile ) as fp :
159
164
data = json .load (fp )
160
165
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 :
164
167
yield from self ._process_packages_v1 (lockfile , data )
165
- elif data ['lockfileVersion' ] in { 3 } :
168
+ elif data ['lockfileVersion' ] in _LOCKFILE_V2_VERSIONS :
166
169
yield from self ._process_packages_v2 (lockfile , data )
167
170
else :
168
171
raise NotImplementedError (
@@ -426,14 +429,21 @@ def _finalize(self) -> None:
426
429
427
430
if self .git_sources :
428
431
# 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.
429
438
scripts = {
430
439
'package.json' : r"""
431
440
walk(
432
441
if type == "object"
433
442
then
434
443
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])"
437
447
else .
438
448
end
439
449
) | from_entries
@@ -443,16 +453,34 @@ def _finalize(self) -> None:
443
453
""" ,
444
454
'package-lock.json' : r"""
445
455
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
449
463
else .
450
464
end
451
465
)
452
466
""" ,
453
467
}
454
468
455
469
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
+
456
484
prefix = self .relative_lockfile_dir (lockfile )
457
485
data : Dict [str , Dict [str , str ]] = {
458
486
'package.json' : {},
@@ -486,6 +514,7 @@ def _finalize(self) -> None:
486
514
patch_commands [lockfile ].append (
487
515
'jq'
488
516
' --arg buildroot "$FLATPAK_BUILDER_BUILDDIR"'
517
+ f' --arg match_against { match_against [filename ]} '
489
518
f' --argjson data { shlex .quote (json_data )} '
490
519
f' { shlex .quote (script )} { target } '
491
520
f' > { target } .new'
0 commit comments