import * as fs from 'fs' ; import { basename , dirname , join , relative } from 'path' ; import * as ts from 'typescript' ; import ResolutionError from '@error/Resolution' ; import { DeclarationInterface as Interface , File } from '@lib/convert' ; import Path from '@lib/Path' ; export interface IDerivedOptions < T extends Interface > { declaration : T ; file : File ; } export interface IOptions < T extends Interface > extends IDerivedOptions < T > { path : string ; } function isBuiltinModule ( module ) { const builtin = [ 'assert' , 'async_hooks' , 'buffer' , 'child_process' , 'cluster' , 'console' , 'constants' , 'crypto' , 'dgram' , 'dns' , 'domain' , 'events' , 'fs' , 'http' , 'http2' , 'https' , 'inspector' , 'module' , 'net' , 'os' , 'path' , 'perf_hooks' , 'process' , 'punycode' , 'querystring' , 'readline' , 'repl' , 'stream' , 'string_decoder' , 'timers' , 'tls' , 'tty' , 'url' , 'util' , 'v8' , 'vm' , 'zlib' , ] ; return builtin . indexOf ( module ) !== - 1 ; } export default abstract class Declaration < T extends Interface > { protected readonly declaration : T ; readonly file : File ; private processed : boolean = false ; readonly original : string ; constructor ( { file , declaration , path } ) { this . declaration = declaration ; this . file = file ; this . original = path ; } get isMapped ( ) : Promise < boolean > { return Promise . resolve ( this . processed ) ; } get module ( ) : Path { return this . file . destination ; } abstract get path ( ) : string ; protected abstract update ( value ) ; toString ( ) { return `${ this . module }: ${ this . path }` ; } async map ( options ) { if ( this . processed ) { return true ; } const { resolvedModule : resolved } = ts . nodeModuleNameResolver ( this . path , this . file . source . toString ( ) , options , { fileExists : fs . existsSync , readFile : ( f ) => fs . readFileSync ( f , 'utf8' ) , } ) ; this . processed = true ; if ( isBuiltinModule ( this . path ) ) { return false ; } else if ( ! resolved ) { throw new ResolutionError ( { declaration : this , module : this . file . source , path : this . path } ) ; } const { resolvedFileName : pathWithExtension , extension , isExternalLibraryImport : external } = resolved ; const path = join ( dirname ( pathWithExtension ) , basename ( pathWithExtension , extension ) ) ; let resolution = relative ( this . file . source . parent . toString ( ) , path ) ; if ( ! resolution . startsWith ( '.' ) ) { resolution = `./${ resolution }` ; } if ( ! external && ( this . path !== resolution ) ) { this . update ( resolution ) ; return true ; } return false ; } }