@@ -31,6 +31,41 @@ function normalizeExtras(inner: string): string {
3131 return deduped . length > 0 ? `[${ deduped . join ( ',' ) } ]` : '' ;
3232}
3333
34+ /** ` >= 2 ; python_version < "3.13"` becomes `>=2 ; python_version<"3.13"`. */
35+ function normalizeRequirementTail ( value : string ) : string {
36+ let result = '' ;
37+ let unquoted = '' ;
38+ let quote : "'" | '"' | undefined ;
39+ let escaped = false ;
40+
41+ const flushUnquoted = ( ) => {
42+ result += unquoted . replace ( / \s + / g, ' ' ) . replace ( / \s * ( [ < > = ! ~ ] = ? ) \s * / g, '$1' ) ;
43+ unquoted = '' ;
44+ } ;
45+
46+ // Preserve PEP 508 marker literals verbatim; a backslash escapes the next character.
47+ for ( const character of value ) {
48+ if ( quote ) {
49+ result += character ;
50+ if ( character === quote && ! escaped ) {
51+ quote = undefined ;
52+ }
53+ escaped = character === '\\' && ! escaped ;
54+ if ( character !== '\\' ) {
55+ escaped = false ;
56+ }
57+ } else if ( character === "'" || character === '"' ) {
58+ flushUnquoted ( ) ;
59+ quote = character ;
60+ result += character ;
61+ } else {
62+ unquoted += character ;
63+ }
64+ }
65+ flushUnquoted ( ) ;
66+ return result . trim ( ) ;
67+ }
68+
3469/**
3570 * Canonicalize a PEP 723 dependency entry so common variants of the same
3671 * requirement produce identical strings. Not a full PEP 508 parser — only
@@ -70,10 +105,12 @@ export function normalizeDependency(dep: string): string {
70105 rest = rest . slice ( extrasMatch [ 0 ] . length ) ;
71106 }
72107
73- const compactedRest = rest
74- . replace ( / \s + / g, ' ' )
75- . replace ( / \s * ( [ < > = ! ~ ] = ? ) \s * / g, '$1' )
76- . trim ( ) ;
108+ const directReference = rest . trim ( ) ;
109+ if ( directReference . startsWith ( '@' ) ) {
110+ return `${ name } ${ extras } ${ directReference } ` ;
111+ }
112+
113+ const compactedRest = normalizeRequirementTail ( rest ) ;
77114
78115 return `${ name } ${ extras } ${ compactedRest } ` ;
79116}
0 commit comments