import { valid , validRange , satisfies } from 'semver' ; import { Resolver , ResolutionType } from './Resolver' ; const NPMURLPattern = /^(?:npm:\/\/)?((?:@[\w\.\-]+\/)?\w[\w\.\-]+)(@[^\/]+)?(\/.*)?$/ ; export function splitNPMURL ( url ) { let loaders = extractLoaders ( url ) ; let requestWithoutLoaders = removeLoaders ( url ) ; let match = requestWithoutLoaders . match ( NPMURLPattern ) ; if ( match ) { let [ _ , name , version , pathname ] = match ; return { loaders , name , version , pathname , } ; } } export class DefaultResolver implements Resolver { knownURLs : { [ url ] : string ; } = { } ; npmPathRedirects : { [ nameVersionPath ] : string ; } = { } ; npmKnownPackageVersions : { [ packageName ] : string [ ] ; } = { } ; npmVersionAliases : { [ versionAlias ] : string ; } = { } ; npmDefaultVersions : { [ packageName ] : string ; } = { } ; registerId ( id ) { this . knownURLs [ id ] = id ; } registerResolvedURL ( url , id ) { this . knownURLs [ url ] = id ; let idNPMMatch = removeLoaders ( id ) . match ( NPMURLPattern ) ; if ( idNPMMatch ) { let [ _ , name , version , path = '' ] = idNPMMatch ; let urlNPMMatch = removeLoaders ( url ) . match ( NPMURLPattern ) ; if ( urlNPMMatch ) { let urlNPMVersion = urlNPMMatch [ 2 ] || '' ; if ( urlNPMVersion !== version ) { this . npmVersionAliases [ urlNPMVersion ] = version ; } let urlNPMPath = urlNPMMatch [ 3 ] || '' ; if ( urlNPMPath !== path ) { this . npmPathRedirects [ name + version + urlNPMPath ] = path ; } } if ( ! this . npmKnownPackageVersions [ name ] ) { this . npmKnownPackageVersions [ name ] = [ version ] ; } else { this . npmKnownPackageVersions [ name ] . push ( version ) ; } } } resolve ( request , parentId , packageDependencies ) { let url ; let loaders = extractLoaders ( request ) ; let requestWithoutLoaders = removeLoaders ( request ) ; let npmMatch = requestWithoutLoaders . match ( NPMURLPattern ) ; if ( ! npmMatch ) { url = resolveIfNotPlainOrUrl ( requestWithoutLoaders , parentId ) ; npmMatch = url . match ( NPMURLPattern ) ; } let name , version , path ; if ( npmMatch ) { name = npmMatch [ 1 ] ; version = npmMatch [ 2 ] ; path = npmMatch [ 3 ] || '' ; if ( ! version ) { let packageVersion = packageDependencies [ name ] ; if ( packageVersion ) { packageVersion = '@' + packageDependencies [ name ] ; } version = packageVersion || this . npmDefaultVersions [ name ] || '@latest' ; } if ( this . npmVersionAliases [ name + version ] ) { version = this . npmVersionAliases [ name + version ] ; } else if ( ! valid ( version . slice ( 1 ) ) ) { version = this . getKnownMatchingVersion ( name , version ) || version ; } if ( ! this . npmDefaultVersions [ name ] ) { this . npmDefaultVersions [ name ] = version ; } path = this . npmPathRedirects [ name + version + path ] || path ; url = 'npm://' + name + version + path ; } let id = this . knownURLs [ url ] ; if ( id ) { return { type : ResolutionType . Available as ResolutionType . Available , id : loaders + id } ; } return { type : ResolutionType . LoadFrom as ResolutionType . LoadFrom , url : loaders + url } ; } private getKnownMatchingVersion ( name , range ) { let rangeWithoutSymbol = range . slice ( 1 ) ; if ( validRange ( rangeWithoutSymbol ) ) { let versions = this . npmKnownPackageVersions [ name ] || [ ] ; for ( let i = 0 ; i < versions . length ; i ++ ) { let version = versions [ i ] ; if ( satisfies ( version . slice ( 1 ) , rangeWithoutSymbol ) ) { return version ; } } } } } /**
 * From system.js
 * Copyright (C) 2013-2018 Guy Bedford
 * https://github.com/systemjs/systemjs
 */ const backslashRegEx = /\\/g ; function resolveIfNotPlainOrUrl ( relUrl , parentUrl ) { if ( relUrl . indexOf ( '://' ) >= 0 ) { return relUrl ; } if ( relUrl . indexOf ( '\\' ) !== - 1 ) relUrl = relUrl . replace ( backslashRegEx , '/' ) ; if ( relUrl [ 0 ] === '/' && relUrl [ 1 ] === '/' ) { return parentUrl . slice ( 0 , parentUrl . indexOf ( ':' ) + 1 ) + relUrl ; } else if ( relUrl [ 0 ] === '.' && ( relUrl [ 1 ] === '/' || relUrl [ 1 ] === '.' && ( relUrl [ 2 ] === '/' || relUrl . length === 2 && ( relUrl += '/' ) ) || relUrl . length === 1 && ( relUrl += '/' ) ) || relUrl [ 0 ] === '/' ) { const parentProtocol = parentUrl . slice ( 0 , parentUrl . indexOf ( ':' ) + 1 ) ; let pathname ; if ( parentUrl [ parentProtocol . length + 1 ] === '/' ) { if ( parentProtocol !== 'file:' ) { pathname = parentUrl . slice ( parentProtocol . length + 2 ) ; pathname = pathname . slice ( pathname . indexOf ( '/' ) + 1 ) ; } else { pathname = parentUrl . slice ( 8 ) ; } } else { pathname = parentUrl . slice ( parentProtocol . length + ( parentUrl [ parentProtocol . length ] === '/' ) ) ; } if ( relUrl [ 0 ] === '/' ) return parentUrl . slice ( 0 , parentUrl . length - pathname . length - 1 ) + relUrl ; const segmented = pathname . slice ( 0 , pathname . lastIndexOf ( '/' ) + 1 ) + relUrl ; const output = [ ] ; let segmentIndex = - 1 ; for ( let i = 0 ; i < segmented . length ; i ++ ) { if ( segmentIndex !== - 1 ) { if ( segmented [ i ] === '/' ) { output . push ( segmented . slice ( segmentIndex , i + 1 ) ) ; segmentIndex = - 1 ; } } else if ( segmented [ i ] === '.' ) { if ( segmented [ i + 1 ] === '.' && ( segmented [ i + 2 ] === '/' || i + 2 === segmented . length ) ) { output . pop ( ) ; i += 2 ; } else if ( segmented [ i + 1 ] === '/' || i + 1 === segmented . length ) { i += 1 ; } else { segmentIndex = i ; } } else { segmentIndex = i ; } } if ( segmentIndex !== - 1 ) output . push ( segmented . slice ( segmentIndex ) ) ; return parentUrl . slice ( 0 , parentUrl . length - pathname . length ) + output . join ( '' ) ; } } function extractLoaders ( url ) { let match = url . match ( /^(.*!)*/ ) ; return match ? match [ 0 ] : '' ; } function removeLoaders ( url ) { return url . replace ( /^(.*!)*/ , '' ) ; }