namespace ts . JsTyping { export interface TypingResolutionHost { directoryExists ( path ) : boolean ; fileExists ( fileName ) : boolean ; readFile ( path , encoding ? ) : string | undefined ; readDirectory ( rootDir , extensions , excludes , includes , depth ? ) : string [ ] ; } interface PackageJson { dependencies ? : MapLike < string > ; devDependencies ? : MapLike < string > ; name ? : string ; optionalDependencies ? : MapLike < string > ; peerDependencies ? : MapLike < string > ; types ? : string ; typings ? : string ; } export interface CachedTyping { typingLocation : string ; version : Version ; } export function isTypingUpToDate ( cachedTyping , availableTypingVersions ) { const availableVersion = new Version ( getProperty ( availableTypingVersions , `ts${ versionMajorMinor }` ) || getProperty ( availableTypingVersions , "latest" ) ! ) ; return availableVersion . compareTo ( cachedTyping . version ) <= 0 ; } const unprefixedNodeCoreModuleList = [ "assert" , "assert/strict" , "async_hooks" , "buffer" , "child_process" , "cluster" , "console" , "constants" , "crypto" , "dgram" , "diagnostics_channel" , "dns" , "dns/promises" , "domain" , "events" , "fs" , "fs/promises" , "http" , "https" , "http2" , "inspector" , "module" , "net" , "os" , "path" , "perf_hooks" , "process" , "punycode" , "querystring" , "readline" , "repl" , "stream" , "stream/promises" , "string_decoder" , "timers" , "timers/promises" , "tls" , "trace_events" , "tty" , "url" , "util" , "util/types" , "v8" , "vm" , "wasi" , "worker_threads" , "zlib" ] ; export const prefixedNodeCoreModuleList = unprefixedNodeCoreModuleList . map ( name => `node:${ name }` ) ; export const nodeCoreModuleList = [ ... unprefixedNodeCoreModuleList , ... prefixedNodeCoreModuleList ] ; export const nodeCoreModules = new Set ( nodeCoreModuleList ) ; export function nonRelativeModuleNameForTypingCache ( moduleName ) { return nodeCoreModules . has ( moduleName ) ? "node" : moduleName ; } /**
     * A map of loose file names to library names that we are confident require typings
     */ export type SafeList = ReadonlyESMap < string , string > ; export function loadSafeList ( host , safeListPath ) { const result = readConfigFile ( safeListPath , path => host . readFile ( path ) ) ; return new Map ( getEntries < string > ( result . config ) ) ; } export function loadTypesMap ( host , typesMapPath ) { const result = readConfigFile ( typesMapPath , path => host . readFile ( path ) ) ; if ( result . config ) { return new Map ( getEntries < string > ( result . config . simpleMap ) ) ; } return undefined ; } param host param fileNames param projectRootPath param safeListPath param packageNameToTypingLocation param typeAcquisition param compilerOptions export function discoverTypings ( host , log , fileNames , projectRootPath , safeList , packageNameToTypingLocation , typeAcquisition , unresolvedImports , typesRegistry ) { if ( ! typeAcquisition || ! typeAcquisition . enable ) { return { cachedTypingPaths : [ ] , newTypingNames : [ ] , filesToWatch : [ ] } ; } const inferredTypings = new Map < string , string > ( ) ; fileNames = mapDefined ( fileNames , fileName => { const path = normalizePath ( fileName ) ; if ( hasJSFileExtension ( path ) ) { return path ; } } ) ; const filesToWatch = [ ] ; if ( typeAcquisition . include ) addInferredTypings ( typeAcquisition . include , "Explicitly included types" ) ; const exclude = typeAcquisition . exclude || [ ] ; const possibleSearchDirs = new Set ( fileNames . map ( getDirectoryPath ) ) ; possibleSearchDirs . add ( projectRootPath ) ; possibleSearchDirs . forEach ( ( searchDir ) => { getTypingNames ( searchDir , "bower.json" , "bower_components" , filesToWatch ) ; getTypingNames ( searchDir , "package.json" , "node_modules" , filesToWatch ) ; } ) ; if ( ! typeAcquisition . disableFilenameBasedTypeAcquisition ) { getTypingNamesFromSourceFileNames ( fileNames ) ; } if ( unresolvedImports ) { const module = deduplicate < string > ( unresolvedImports . map ( nonRelativeModuleNameForTypingCache ) , equateStringsCaseSensitive , compareStringsCaseSensitive ) ; addInferredTypings ( module , "Inferred typings from unresolved imports" ) ; } packageNameToTypingLocation . forEach ( ( typing , name ) => { const registryEntry = typesRegistry . get ( name ) ; if ( inferredTypings . has ( name ) && inferredTypings . get ( name ) === undefined && registryEntry !== undefined && isTypingUpToDate ( typing , registryEntry ) ) { inferredTypings . set ( name , typing . typingLocation ) ; } } ) ; for ( const excludeTypingName of exclude ) { const didDelete = inferredTypings . delete ( excludeTypingName ) ; if ( didDelete && log ) log ( `Typing for ${ excludeTypingName } is in exclude list, will be ignored.` ) ; } const newTypingNames = [ ] ; const cachedTypingPaths = [ ] ; inferredTypings . forEach ( ( inferred , typing ) => { if ( inferred !== undefined ) { cachedTypingPaths . push ( inferred ) ; } else { newTypingNames . push ( typing ) ; } } ) ; const result = { cachedTypingPaths , newTypingNames , filesToWatch } ; if ( log ) log ( `Result: ${ JSON . stringify ( result ) }` ) ; return result ; function addInferredTyping ( typingName ) { if ( ! inferredTypings . has ( typingName ) ) { inferredTypings . set ( typingName , undefined ! ) ; } } function addInferredTypings ( typingNames , message ) { if ( log ) log ( `${ message }: ${ JSON . stringify ( typingNames ) }` ) ; forEach ( typingNames , addInferredTyping ) ; } param projectRootPath param manifestName param modulesDirName param filesToWatch function getTypingNames ( projectRootPath , manifestName , modulesDirName , filesToWatch ) { const manifestPath = combinePaths ( projectRootPath , manifestName ) ; let manifest ; let manifestTypingNames ; if ( host . fileExists ( manifestPath ) ) { filesToWatch . push ( manifestPath ) ; manifest = readConfigFile ( manifestPath , path => host . readFile ( path ) ) . config ; manifestTypingNames = flatMap ( [ manifest . dependencies , manifest . devDependencies , manifest . optionalDependencies , manifest . peerDependencies ] , getOwnKeys ) ; addInferredTypings ( manifestTypingNames , `Typing names in '${ manifestPath }' dependencies` ) ; } const packagesFolderPath = combinePaths ( projectRootPath , modulesDirName ) ; filesToWatch . push ( packagesFolderPath ) ; if ( ! host . directoryExists ( packagesFolderPath ) ) { return ; } const packageNames = [ ] ; const dependencyManifestNames = manifestTypingNames ? manifestTypingNames . map ( typingName => combinePaths ( packagesFolderPath , typingName , manifestName ) ) : host . readDirectory ( packagesFolderPath , [ Extension . Json ] , undefined , undefined , 3 ) . filter ( manifestPath => { if ( getBaseFileName ( manifestPath ) !== manifestName ) { return false ; } const pathComponents = getPathComponents ( normalizePath ( manifestPath ) ) ; const isScoped = pathComponents [ pathComponents . length - 3 ] [ 0 ] === "@" ; return isScoped && pathComponents [ pathComponents . length - 4 ] . toLowerCase ( ) === modulesDirName || ! isScoped && pathComponents [ pathComponents . length - 3 ] . toLowerCase ( ) === modulesDirName ; } ) ; if ( log ) log ( `Searching for typing names in ${ packagesFolderPath }; all files: ${ JSON . stringify ( dependencyManifestNames ) }` ) ; for ( const manifestPath of dependencyManifestNames ) { const normalizedFileName = normalizePath ( manifestPath ) ; const result = readConfigFile ( normalizedFileName , ( path ) => host . readFile ( path ) ) ; const manifest = result . config ; if ( ! manifest . name ) { continue ; } const ownTypes = manifest . types || manifest . typings ; if ( ownTypes ) { const absolutePath = getNormalizedAbsolutePath ( ownTypes , getDirectoryPath ( normalizedFileName ) ) ; if ( host . fileExists ( absolutePath ) ) { if ( log ) log ( `    Package '${ manifest . name }' provides its own types.` ) ; inferredTypings . set ( manifest . name , absolutePath ) ; } else { if ( log ) log ( `    Package '${ manifest . name }' provides its own types but they are missing.` ) ; } } else { packageNames . push ( manifest . name ) ; } } addInferredTypings ( packageNames , "    Found package names" ) ; } param fileNames function getTypingNamesFromSourceFileNames ( fileNames ) { const fromFileNames = mapDefined ( fileNames , j => { if ( ! hasJSFileExtension ( j ) ) return undefined ; const inferredTypingName = removeFileExtension ( getBaseFileName ( j . toLowerCase ( ) ) ) ; const cleanedTypingName = removeMinAndVersionNumbers ( inferredTypingName ) ; return safeList . get ( cleanedTypingName ) ; } ) ; if ( fromFileNames . length ) { addInferredTypings ( fromFileNames , "Inferred typings from file names" ) ; } const hasJsxFile = some ( fileNames , f => fileExtensionIs ( f , Extension . Jsx ) ) ; if ( hasJsxFile ) { if ( log ) log ( `Inferred 'react' typings due to presence of '.jsx' extension` ) ; addInferredTyping ( "react" ) ; } } } export const enum NameValidationResult { Ok , EmptyName , NameTooLong , NameStartsWithDot , NameStartsWithUnderscore , NameContainsNonURISafeCharacters } const maxPackageNameLength = 214 ; export interface ScopedPackageNameValidationResult { name : string ; isScopeName : boolean ; result : NameValidationResult ; } export type PackageNameValidationResult = NameValidationResult | ScopedPackageNameValidationResult ; /**
     * Validates package name using rules defined at https://docs.npmjs.com/files/package.json
     */ export function validatePackageName ( packageName ) { return validatePackageNameWorker ( packageName , true ) ; } function validatePackageNameWorker ( packageName , supportScopedPackage ) ; function validatePackageNameWorker ( packageName , supportScopedPackage ) ; function validatePackageNameWorker ( packageName , supportScopedPackage ) { if ( ! packageName ) { return NameValidationResult . EmptyName ; } if ( packageName . length > maxPackageNameLength ) { return NameValidationResult . NameTooLong ; } if ( packageName . charCodeAt ( 0 ) === CharacterCodes . dot ) { return NameValidationResult . NameStartsWithDot ; } if ( packageName . charCodeAt ( 0 ) === CharacterCodes . _ ) { return NameValidationResult . NameStartsWithUnderscore ; } if ( supportScopedPackage ) { const matches = /^@([^/]+)\/([^/]+)$/ . exec ( packageName ) ; if ( matches ) { const scopeResult = validatePackageNameWorker ( matches [ 1 ] , false ) ; if ( scopeResult !== NameValidationResult . Ok ) { return { name : matches [ 1 ] , isScopeName : true , result : scopeResult } ; } const packageResult = validatePackageNameWorker ( matches [ 2 ] , false ) ; if ( packageResult !== NameValidationResult . Ok ) { return { name : matches [ 2 ] , isScopeName : false , result : packageResult } ; } return NameValidationResult . Ok ; } } if ( encodeURIComponent ( packageName ) !== packageName ) { return NameValidationResult . NameContainsNonURISafeCharacters ; } return NameValidationResult . Ok ; } export function renderPackageNameValidationFailure ( result , typing ) { return typeof result === "object" ? renderPackageNameValidationFailureWorker ( typing , result . result , result . name , result . isScopeName ) : renderPackageNameValidationFailureWorker ( typing , result , typing , false ) ; } function renderPackageNameValidationFailureWorker ( typing , result , name , isScopeName ) { const kind = isScopeName ? "Scope" : "Package" ; switch ( result ) { case NameValidationResult . EmptyName : return `'${ typing }':: ${ kind } name '${ name }' cannot be empty` ; case NameValidationResult . NameTooLong : return `'${ typing }':: ${ kind } name '${ name }' should be less than ${ maxPackageNameLength } characters` ; case NameValidationResult . NameStartsWithDot : return `'${ typing }':: ${ kind } name '${ name }' cannot start with '.'` ; case NameValidationResult . NameStartsWithUnderscore : return `'${ typing }':: ${ kind } name '${ name }' cannot start with '_'` ; case NameValidationResult . NameContainsNonURISafeCharacters : return `'${ typing }':: ${ kind } name '${ name }' contains non URI safe characters` ; case NameValidationResult . Ok : return Debug . fail ( ) ; default : throw Debug . assertNever ( result ) ; } } }