import * as debugLib from 'debug' ; import { standardizePackageName } from '../../../standardize-package-name' ; const debug = debugLib ( 'snyk-fix:python:requirements-file-parser' ) ; type VersionComparator = '<' | '<=' | '!=' | '==' | '>=' | '>' | '~=' ; export interface Requirement { originalText : string ; line : number ; name ? : string ; originalName ? : string ; versionComparator ? : VersionComparator ; version ? : string ; extras ? : string ; } param requirementsFile export interface ParsedRequirements { requirements : Requirement [ ] ; endsWithNewLine : boolean ; } export function parseRequirementsFile ( requirementsFile ) { const endsWithNewLine = requirementsFile . endsWith ( '\n' ) ; const lines = requirementsFile . replace ( /\n$/ , '' ) . split ( '\n' ) ; const requirements = [ ] ; lines . map ( ( requirementText , line ) => { const requirement = extractDependencyDataFromLine ( requirementText , line ) ; if ( requirement ) { requirements . push ( requirement ) ; } } ) ; return { requirements , endsWithNewLine } ; } function extractDependencyDataFromLine ( requirementText , line ) { try { const requirement = { originalText : requirementText , line } ; const trimmedText = requirementText . trim ( ) ; if ( requirementText === '' || trimmedText . startsWith ( '-e' ) || trimmedText . startsWith ( '#' ) || trimmedText . startsWith ( 'file:' ) ) { return requirement ; } const regex = /([A-Z0-9-._]*)(!=|===|==|>=|<=|>|<|~=)(\d*\.?\d*\.?\d*[A-Z0-9]*)(.*)/i ; const result = regex . exec ( requirementText ) ; if ( result !== null ) { requirement . name = standardizePackageName ( result [ 1 ] ) ; requirement . originalName = result [ 1 ] ; requirement . versionComparator = result [ 2 ] as VersionComparator ; requirement . version = result [ 3 ] ; requirement . extras = result [ 4 ] ; } if ( ! ( requirement . version && requirement . name ) ) { throw new Error ( 'Failed to extract dependency data' ) ; } return requirement ; } catch ( err ) { debug ( { error : err . message , requirementText , line } , 'failed to parse requirement' ) ; return { originalText : requirementText , line } ; } }