import { FS , getDefinitelyTyped } from "./get-definitely-typed" ; import { Options } from "./lib/common" ; import { NpmInfoRawVersions , NpmInfoVersion , UncachedNpmInfoClient } from "./lib/npm-client" ; import { AllPackages , formatTypingVersion , TypingsData , TypingVersion } from "./lib/packages" ; import { Semver } from "./lib/versions" ; import { Logger , logger , loggerWithErrors , writeLog } from "./util/logging" ; import { assertDefined , best , logUncaughtErrors , mapDefined , nAtATime } from "./util/util" ; if ( ! module . parent ) { const log = loggerWithErrors ( ) [ 0 ] ; logUncaughtErrors ( async ( ) => checkParseResults ( true , await getDefinitelyTyped ( Options . defaults , log ) , Options . defaults , new UncachedNpmInfoClient ( ) ) ) ; } export default async function checkParseResults ( includeNpmChecks , dt , options , client ) { const allPackages = await AllPackages . read ( dt ) ; const [ log , logResult ] = logger ( ) ; checkTypeScriptVersions ( allPackages ) ; checkPathMappings ( allPackages ) ; const dependedOn = new Set < string > ( ) ; const packages = allPackages . allPackages ( ) ; for ( const pkg of packages ) { if ( pkg instanceof TypingsData ) { for ( const dep of pkg . dependencies ) { dependedOn . add ( dep . name ) ; } for ( const dep of pkg . testDependencies ) { dependedOn . add ( dep ) ; } } } if ( includeNpmChecks ) { await nAtATime ( 10 , allPackages . allTypings ( ) , pkg => checkNpm ( pkg , log , dependedOn , client ) , { name : "Checking for typed packages..." , flavor : pkg => pkg . desc , options , } ) ; } await writeLog ( "conflicts.md" , logResult ( ) ) ; } function checkTypeScriptVersions ( allPackages ) { for ( const pkg of allPackages . allTypings ( ) ) { for ( const dep of allPackages . allDependencyTypings ( pkg ) ) { if ( dep . minTypeScriptVersion > pkg . minTypeScriptVersion ) { throw new Error ( `${ pkg . desc } depends on ${ dep . desc } but has a lower required TypeScript version.` ) ; } } } } function checkPathMappings ( allPackages ) { for ( const pkg of allPackages . allTypings ( ) ) { const pathMappings = new Map < string , TypingVersion > ( pkg . pathMappings . map ( p => [ p . packageName , p . version ] ) ) ; const unusedPathMappings = new Set ( pathMappings . keys ( ) ) ; for ( const dependency of allPackages . allDependencyTypings ( pkg ) ) { for ( const { packageName : transitiveDependencyName , version : transitiveDependencyVersion } of dependency . pathMappings ) { const pathMappingVersion = pathMappings . get ( transitiveDependencyName ) ; if ( pathMappingVersion && ( pathMappingVersion . major !== transitiveDependencyVersion . major || pathMappingVersion . minor !== transitiveDependencyVersion . minor ) ) { const expectedPathMapping = `${ transitiveDependencyName }/v${ formatTypingVersion ( transitiveDependencyVersion ) }` ; throw new Error ( `${ pkg . desc } depends on ${ dependency . desc }, which has a path mapping for ${ expectedPathMapping }. ` + `${ pkg . desc } must have the same path mappings as its dependencies.` ) ; } unusedPathMappings . delete ( transitiveDependencyName ) ; } unusedPathMappings . delete ( dependency . name ) ; } for ( const unusedPathMapping of unusedPathMappings ) { if ( pkg . name !== unusedPathMapping ) { throw new Error ( `${ pkg . desc } has unused path mapping for ${ unusedPathMapping }` ) ; } } } } async function checkNpm ( { major , minor , name , libraryName , projectName , contributors } , log , dependedOn , client ) { if ( notNeededExceptions . has ( name ) ) { return ; } const info = await client . fetchRawNpmInfo ( name ) ; if ( ! info ) { return ; } const versions = getRegularVersions ( info . versions ) ; const firstTypedVersion = best ( mapDefined ( versions , ( { hasTypes , version } ) => hasTypes ? version : undefined ) , ( a , b ) => b . greaterThan ( a ) ) ; if ( firstTypedVersion === undefined || ! best ( versions , ( a , b ) => a . version . greaterThan ( b . version ) ) ! . hasTypes ) { return ; } const ourVersion = `${ major }.${ minor }` ; log ( "" ) ; log ( `Typings already defined for ${ name } (${ libraryName }) as of ${ firstTypedVersion . versionString } (our version: ${ ourVersion })` ) ; const contributorUrls = contributors . map ( c => { const gh = "https://github.com/" ; return c . url . startsWith ( gh ) ? `@${ c . url . slice ( gh . length ) }` : `${ c . name } (${ c . url })` ; } ) . join ( ", " ) ; log ( "  To fix this:" ) ; log ( `  git checkout -b not-needed-${ name }` ) ; const yarnargs = [ name , firstTypedVersion . versionString , projectName ] ; if ( libraryName !== name ) { yarnargs . push ( JSON . stringify ( libraryName ) ) ; } log ( "  yarn not-needed " + yarnargs . join ( " " ) ) ; log ( `  git add --all && git commit -m "${ name }: Provides its own types" && git push -u origin not-needed-${ name }` ) ; log ( `  And comment PR: This will deprecate \`@types/${ name }\` in favor of just \`${ name }\`. CC ${ contributorUrls }` ) ; if ( new Semver ( major , minor , 0 ) . greaterThan ( firstTypedVersion ) ) { log ( "  WARNING: our version is greater!" ) ; } if ( dependedOn . has ( name ) ) { log ( "  WARNING: other packages depend on this!" ) ; } } export async function packageHasTypes ( packageName , client ) { const info = assertDefined ( await client . fetchRawNpmInfo ( packageName ) ) ; return versionHasTypes ( info . versions [ info [ "dist-tags" ] . latest ] ) ; } function getRegularVersions ( versions ) { return mapDefined ( Object . entries ( versions ) , ( [ versionString , info ] ) => { const version = Semver . tryParse ( versionString ) ; return version === undefined ? undefined : { version , hasTypes : versionHasTypes ( info ) } ; } ) ; } function versionHasTypes ( info ) { return "types" in info || "typings" in info ; } const notNeededExceptions = new Set ( [ "angular-ui-router" , "ui-router-extras" , "angular-touchspin" , "bingmaps" , "dwt" , "epub" , "expr-eval" , "express-serve-static-core" , "immutability-helper" , "node-mysql-wrapper" , "raspi" , "raspi-board" , "raspi-gpio" , "raspi-i2c" , "raspi-led" , "raspi-onewire" , "raspi-peripheral" , "raspi-pwm" , "raspi-serial" , "raspi-soft-pwm" , "stampit" , ] ) ;