fsWatchFile.mjs

// import fs from 'fs'
// import crypto from 'crypto'
import chokidar from 'chokidar'
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import genID from './genID.mjs'
import now2str from './now2str.mjs'
import evem from './evem.mjs'
import ispint from './ispint.mjs'
import isbol from './isbol.mjs'
import isfun from './isfun.mjs'
import cint from './cint.mjs'


/**
 * 後端nodejs基於chokidar提供偵測檔案內容變更或出現或消失事件之EventEmitter
 *
 * Unit Test: {@link https://github.com/yuda-lyu/wsemi/blob/master/test/fsWatchFile.test.mjs Github}
 * @memberOf wsemi
 * @param {Object} [opt={}] 輸入設定物件,預設{}
 * @param {Boolean} [opt.polling=false] 輸入是否使用輪循布林值,代表chokidar的usePolling,預設為false
 * @param {Integer} [opt.timeInterval=100] 輸入當polling為true時偵測檔案變更間隔時間整數,代表chokidar開啟polling時的interval,單位為毫秒ms,預設為100
 * @param {Integer} [opt.timeBinaryInterval=300] 輸入當polling為true時偵測二進位檔案變更間隔時間整數,代表chokidar開啟polling時的binaryInterval,單位為毫秒ms,預設為300
 * @example
 * need test in nodejs.
 *
 * let evfl = fsWatchFile()
 *
 * evfl.on('./abc.json', (msg) => {
 *     console.log(msg.type, ':', msg.fp)
 *     // => add : ./abc.json
 *     // change : ./abc.json
 *     // unlink : ./abc.json
 * })
 *
 * // evfl.clear()
 *
 */
function fsWatchFile(opt = {}) {
    let kpWhr = {}

    //ev
    let ev = evem()

    //polling
    let polling = get(opt, 'polling')
    if (!isbol(polling)) {
        polling = false
    }

    //timeInterval
    let timeInterval = get(opt, 'timeInterval')
    if (!ispint(timeInterval)) {
        timeInterval = 100
    }
    timeInterval = cint(timeInterval)

    //timeBinaryInterval
    let timeBinaryInterval = get(opt, 'timeBinaryInterval')
    if (!ispint(timeBinaryInterval)) {
        timeBinaryInterval = 300
    }
    timeBinaryInterval = cint(timeBinaryInterval)

    // function getTag(fp) {
    //     let tag = ''
    //     try {

    //         //readFileSync
    //         let bin = fs.readFileSync(fp)

    //         //tag
    //         tag = crypto.createHash('md5').update(bin).digest('base64')

    //     }
    //     catch (err) {}
    //     return tag
    // }

    function watchEvent(fp, fun) {

        //watch
        let watcher = chokidar.watch(fp, {
            // persistent: true,
            // ignoreInitial: false,
            usePolling: polling,
            interval: timeInterval,
            binaryInterval: timeBinaryInterval,
            awaitWriteFinish: true, //須比較多延遲偵測檔案是否變更完成, 但對於連鎖驅動比較保險
            // depth: undefined,
        })

        //id
        let id = genID()

        //save
        kpWhr[id] = {
            id,
            fp,
            watcher,
        }

        //on
        watcher
            .on('all', (type, rfp, stats) => {
                // console.log(type, rfp, stats)
                //type=add,addDir,change,unlink,unlinkDir, 注意當unlinkDir後就不會再觸發變更事件, 此處只處理file

                //check
                if (type === 'addDir' || type === 'unlinkDir') {
                    return
                }

                //fun
                if (isfun(fun)) {
                    fun({
                        fp,
                        type,
                        time: now2str(),
                        stats,
                    })
                }

            })

    }

    function unWatchEventCore(v, k) {
        v.watcher.close()
            // .then(() => {})
            .catch((err) => {
                console.log(err)
                console.log('watcher.close error', v.fp)
            })
            .finally(() => {
                delete kpWhr[k]
            })
    }

    function unWatchEvent(fp) {
        each(kpWhr, (v, k) => {
            if (v.fp === fp) {
                unWatchEventCore(v, k)
            }
        })
    }

    function clear() {
        each(kpWhr, (v, k) => {
            unWatchEventCore(v, k)
        })
    }

    //save
    ev.on = watchEvent
    ev.off = unWatchEvent
    ev.clear = clear

    return ev
}

export default fsWatchFile