import request from 'request' ; import requestPromise from 'request-promise-native' ; import fs from 'fs-extra' ; import path from 'path' ; import { imageSize } from 'image-size' ; import { IImgSize , IPathTransformedImgInfo , IPluginNameType , ILogger } from '../types' ; import { URL } from 'url' ; export const isUrl = ( url ) : boolean => ( url . startsWith ( 'http://' ) || url . startsWith ( 'https://' ) ) ; export const isUrlEncode = ( url ) : boolean => { url = url || '' ; try { return url !== decodeURI ( url ) ; } catch ( e ) { return true ; } } ; export const handleUrlEncode = ( url ) : string => { if ( ! isUrlEncode ( url ) ) { url = encodeURI ( url ) ; } return url ; } ; export const getImageSize = ( file ) : IImgSize => { try { const { width = 0 , height = 0 } = imageSize ( file ) ; return { real : true , width , height } ; } catch ( e ) { return { real : false , width : 200 , height : 200 } ; } } ; export const getFSFile = async ( filePath ) : Promise < IPathTransformedImgInfo > => { try { return { extname : path . extname ( filePath ) , fileName : path . basename ( filePath ) , buffer : await fs . readFile ( filePath ) , success : true } ; } catch { return { reason : `read file ${ filePath } error` , success : false } ; } } ; export const getURLFile = async ( url ) : Promise < IPathTransformedImgInfo > => { const requestOptions = { method : 'GET' , url : handleUrlEncode ( url ) , encoding : null } ; let isImage = false ; let extname = '' ; let timeoutId ; const requestFn = new Promise < IPathTransformedImgInfo > ( ( resolve , reject ) => { ( async ( ) => { try { const res = await requestPromise ( requestOptions ) . on ( 'response' , ( response ) : void => { const contentType = response . headers [ 'content-type' ] ; if ( contentType ?. includes ( 'image' ) ) { isImage = true ; extname = `.${ contentType . split ( 'image/' ) [ 1 ] }` ; } } ) ; clearTimeout ( timeoutId ) ; if ( isImage ) { const urlPath = new URL ( requestOptions . url ) . pathname ; resolve ( { buffer : res , fileName : path . basename ( urlPath ) , extname , success : true } ) ; } else { resolve ( { success : false , reason : `${ url } is not image` } ) ; } } catch { clearTimeout ( timeoutId ) ; resolve ( { success : false , reason : `request ${ url } error` } ) ; } } ) ( ) . catch ( reject ) ; } ) ; const timeoutPromise = new Promise < IPathTransformedImgInfo > ( ( resolve ) : void => { timeoutId = setTimeout ( ( ) => { resolve ( { success : false , reason : `request ${ url } timeout` } ) ; } , 10000 ) ; } ) ; return Promise . race ( [ requestFn , timeoutPromise ] ) ; } ; param name export const getPluginNameType = ( name ) : IPluginNameType => { if ( /^@[^/]+\/picgo-plugin-/ . test ( name ) ) { return 'scope' ; } else if ( name . startsWith ( 'picgo-plugin-' ) ) { return 'normal' ; } else if ( isSimpleName ( name ) ) { return 'simple' ; } return 'unknown' ; } ; param name export const isSimpleName = ( nameOrPath ) : boolean => { if ( path . isAbsolute ( nameOrPath ) ) { return false ; } const pluginPath = path . join ( process . cwd ( ) , nameOrPath ) ; if ( fs . existsSync ( pluginPath ) ) { return false ; } if ( nameOrPath . includes ( '/' ) || nameOrPath . includes ( '\\' ) ) { return false ; } return true ; } ; param name export const handleStreamlinePluginName = ( name ) : string => { if ( /^@[^/]+\/picgo-plugin-/ . test ( name ) ) { return name . replace ( /^@[^/]+\/picgo-plugin-/ , '' ) ; } else { return name . replace ( /picgo-plugin-/ , '' ) ; } } ; param name param scope export const handleCompletePluginName = ( name , scope = '' ) : string => { if ( scope ) { return `@${ scope }/picgo-plugin-${ name }` ; } else { return `picgo-plugin-${ name }` ; } } ; param nameOrPath export const getProcessPluginName = ( nameOrPath , logger = console ) : string => { const pluginNameType = getPluginNameType ( nameOrPath ) ; switch ( pluginNameType ) { case 'normal' : case 'scope' : return nameOrPath ; case 'simple' : return handleCompletePluginName ( nameOrPath ) ; default : { let pluginPath = nameOrPath ; if ( path . isAbsolute ( nameOrPath ) && fs . existsSync ( nameOrPath ) ) { return handleUnixStylePath ( pluginPath ) ; } pluginPath = path . join ( process . cwd ( ) , nameOrPath ) ; if ( fs . existsSync ( pluginPath ) ) { return handleUnixStylePath ( pluginPath ) ; } logger . warn ( `Can't find plugin ${ nameOrPath }` ) ; return '' ; } } } ; param nameOrPath export const getNormalPluginName = ( nameOrPath , logger = console ) : string => { const pluginNameType = getPluginNameType ( nameOrPath ) ; switch ( pluginNameType ) { case 'normal' : return removePluginVersion ( nameOrPath ) ; case 'scope' : return removePluginVersion ( nameOrPath , true ) ; case 'simple' : return removePluginVersion ( handleCompletePluginName ( nameOrPath ) ) ; default : { if ( ! fs . existsSync ( nameOrPath ) ) { logger . warn ( `Can't find plugin: ${ nameOrPath }` ) ; return '' ; } const packageJSONPath = path . posix . join ( nameOrPath , 'package.json' ) ; if ( ! fs . existsSync ( packageJSONPath ) ) { logger . warn ( `Can't find plugin: ${ nameOrPath }` ) ; return '' ; } else { const pkg = fs . readJSONSync ( packageJSONPath ) || { } ; if ( ! pkg . name ?. includes ( 'picgo-plugin-' ) ) { logger . warn ( `The plugin package.json's name filed is ${ pkg . name as string || 'empty' }, need to include the prefix: picgo-plugin-` ) ; return '' ; } return pkg . name ; } } } } ; param path export const handleUnixStylePath = ( pathStr ) : string => { const pathArr = pathStr . split ( path . sep ) ; return pathArr . join ( '/' ) ; } ; param nameOrPath param scope export const removePluginVersion = ( nameOrPath , scope = false ) : string => { if ( ! nameOrPath . includes ( '@' ) ) { return nameOrPath ; } else { let reg = /(.+\/)?(picgo-plugin-\w+)(@.+)*/ ; if ( scope ) { reg = /(.+\/)?(^@[^/]+\/picgo-plugin-\w+)(@.+)*/ ; } const matchArr = nameOrPath . match ( reg ) ; if ( ! matchArr ) { console . warn ( 'can not remove plugin version' ) ; return nameOrPath ; } else { return matchArr [ 2 ] ; } } } ; /**
 * the config black item list which won't be setted
 * only can be got
 */ export const configBlackList = [ ] ; param key export const isConfigKeyInBlackList = ( key ) : boolean => { return configBlackList . some ( blackItem => key . startsWith ( blackItem ) ) ; } ; param config returns export const isInputConfigValid = ( config ) : boolean => { if ( typeof config === 'object' && ! Array . isArray ( config ) && Object . keys ( config ) . length > 0 ) { return true ; } return false ; } ;