// import path from 'path'
import fs from 'fs'
import _ from 'lodash-es'
import w from './wsemip.umd.js'
import pathResolve from './pathResolve.mjs'
import rollupWorker from './rollupWorker.mjs'
import rollupFile from './rollupFile.mjs'
function genParforCoreCode(fpPmSeries, fpCore) {
let c = `
import pmSeries from '{fpPmSeries}'
import core from '{fpCore}'
async function parforCore(pgs) {
//pmSeries
let rs = await pmSeries(pgs, async (pg, kpg) => {
let { v, k } = pg
let r = await core(v)
return {
k,
r,
}
})
// console.log('parforCore rs', rs)
return rs
}
export default parforCore
`
if (fs.existsSync(fpPmSeries)) {
fpPmSeries = pathResolve(fpPmSeries)
}
else {
fpPmSeries = fpPmSeries.replace('./', 'w-package-tools/')
}
c = c.replace('{fpPmSeries}', fpPmSeries)
c = c.replace('{fpCore}', pathResolve(fpCore))
return c
}
function genParforCode(fpWpf, fpParforCore) {
let c = `
import get from 'lodash-es/get.js'
import each from 'lodash-es/each.js'
import size from 'lodash-es/size.js'
import w from '{fpWpf}'
import parforCore from '{fpParforCore}'
async function parfor(pgs, opt = {}) {
//cb
let cb = get(opt, 'cb')
//returnResult
let returnResult = get(opt, 'returnResult')
if (!w.isbol(returnResult)) {
returnResult = false
}
//check
if (!returnResult && !w.isfun(cb)) {
return Promise.reject('cb is not a function, need to set opt.returnResult to true')
}
//takeLimit
let takeLimit = get(opt, 'takeLimit')
if (!w.ispint(takeLimit)) {
takeLimit = 4
}
takeLimit = w.cint(takeLimit)
//takeNumOfPgs
let takeNumOfPgs = get(opt, 'takeNumOfPgs')
if (!w.ispint(takeNumOfPgs)) {
takeNumOfPgs = 1
}
takeNumOfPgs = w.cint(takeNumOfPgs)
//arr
let isPgsObj = false
let arr = []
if (w.iseobj(pgs)) {
isPgsObj = true
each(pgs, (v, k) => {
arr.push({ v, k })
})
}
else if (w.isearr(pgs)) {
each(pgs, (v, k) => {
arr.push({ v, k })
})
}
else {
throw new Error('pgs is not an effective array or object')
}
// console.log('arr', size(arr), arr)
//n
let n = size(arr)
//gpgs, takeNumOfPgs
// let m = 0
let gpgs = []
for (let i = 0; i < n; i += takeNumOfPgs) {
let pgs = []
for (let j = 0; j < takeNumOfPgs; j++) {
let k = i + j
if (k < n) {
// m++
pgs.push(arr[k])
}
}
gpgs.push(pgs)
}
// console.log('gpgs', size(gpgs), gpgs)
//rs
let rs = null
if (returnResult) {
if (isPgsObj) {
rs = {}
}
else {
rs = []
}
}
//pmMap
await w.pmMap(gpgs, async (pgs, kpgs) => {
// console.log('pmMap', pgs, kpgs)
//call fun
let prs = await parforCore(pgs)
// console.log('prs', prs)
//save
if (returnResult) {
each(prs, (pr, kpr) => {
rs[pr.k] = pr.r //obj或arr都適用
})
}
//call cb
if (w.isfun(cb)) {
each(prs, (pr, kpr) => {
cb(pr.r, pr.k)
})
}
}, takeLimit)
return rs
}
export default parfor
`
if (fs.existsSync(fpWpf)) {
fpWpf = pathResolve(fpWpf)
}
else {
fpWpf = fpWpf.replace('./', 'w-package-tools/')
}
c = c.replace('{fpWpf}', fpWpf)
c = c.replace('{fpParforCore}', pathResolve(fpParforCore))
return c
}
/**
* 使用rollup轉譯外部指定的核心分析程式碼檔案,並封裝至前端web worker與後端worker內
*
* @param {Object} opt 輸入設定物件
* @param {String} opt.name 輸入模組名稱字串,將來會掛於winodw下或於node引入使用
* @param {String} [opt.type='object'] 輸入模組類型字串,可選'function'、'object'。若使用'function',於初始化後可呼叫terminate銷毀;若使用'object',預設execObjectFunsByInstance為true,執行完指定函數後亦自動銷毀,若改execObjectFunsByInstance為false,就一樣得於初始化後呼叫terminate銷毀。回傳函數或物件。轉譯後會掛載模組名稱至window下,若type使用'function'時則window['模組名稱']為函數,得自己初始化才能呼叫其內函數或監聽事件;若type使用'object'時則window['模組名稱']為物件,可直接呼叫其內函數預設'object'
* @param {Array} opt.funNames 輸入模組可被呼叫的函數名稱陣列
* @param {Array} [opt.evNames=[]] 輸入模組可監聽的函數名稱陣列,預設[]
* @param {String} opt.fpSrc 輸入原始碼檔案位置字串
* @param {String} opt.fpTar 輸入轉譯完程式碼檔案儲存位置字串
* @param {String} [opt.nameDistType=''] 輸入轉譯檔案名稱格式字串,可選'kebabCase',預設''
* @param {Function} [opt.hookNameDist=null] 輸入強制指定轉譯檔案名稱函數,預設null,會複寫nameDistType之處理結果
* @param {String} [opt.formatOut='es'] 輸入欲轉譯成js格式字串,可選'umd'、'iife'、'es',預設'es'
* @param {String} [opt.targets='new'] 輸入轉譯等級字串,可選'new'、'old',預設'new'
* @param {String} [opt.runin='both'] 輸入執行環境字串,可選'both'、'nodejs'、'browser',預設'both'
* @param {Boolean} [opt.bNodePolyfill=false] 輸入轉譯是否自動加入Nodejs polyfill布林值,主要把Nodejs語法(例如fs)轉為瀏覽器端語法,預設false
* @param {Boolean} [opt.execFunctionByInstance=true] 輸入若模組類型為物件type='function'時,是否將function視為使用獨立實體執行並自動銷毀實體布林值,例如原模組就是一個運算函數,不需要回傳eventemmitter監聽事件,預設true
* @param {Boolean} [opt.execObjectFunsByInstance=true] 輸入若模組類型為物件type='object'時,各函式是否使用獨立實體執行布林值,例如使用到stream的各函式會因共用同一個實體導致降速,故各函數需自動有各自實體,預設true
* @param {Boolean} [opt.bMinify=true] 輸入轉譯檔案是否進行壓縮布林值,預設true
* @param {Boolean} [opt.keepFnames=false] 輸入當轉譯檔案需壓縮時,是否保留函數名稱布林值,預設false
* @param {Array} [opt.mangleReserved=[]] 輸入當轉譯檔案需壓縮時,需保留函數名稱或變數名稱陣列,預設[]
* @param {Array} [opt.mainFields=null] 輸入取用套件內環境入口時,可強制給予循序入口陣列,各入口可選'browser'、'module'、'main',給予null則代表用rollup內建,預設null
* @param {Object} [opt.globals={}] 輸入指定內外模組的關聯性物件,預設{}
* @param {Array} [opt.external=[]] 輸入指定內部模組需引用外部模組陣列,預設[]
* @param {Boolean} [opt.bLog=true] 輸入是否顯示預設log布林值,預設true
*/
async function rollupParfor(opt = {}) {
//欲轉譯檔案需使用es6模組語法, 輸出格式為export default, 輸出需為物件, 該物件給予任意可執行之async函數, 打包後會於web worker內執行, 並再把全部async函數映射出來至指定物件當中, 故可採一樣(未封裝前)方式執行
//name
let name = _.get(opt, 'name', null)
if (!w.isestr(name)) {
return Promise.reject('invalid opt.name')
}
//fpSrc
let fpSrc = _.get(opt, 'fpSrc', null)
if (!w.fsIsFile(fpSrc)) {
return Promise.reject('opt.fpSrc is not file')
}
//fpTar
let fpTar = _.get(opt, 'fpTar', null)
if (!w.isestr(fpTar)) {
return Promise.reject('invalid opt.fpTar')
}
//type
let type = _.get(opt, 'type', null)
if (w.isestr(type) && type !== 'function') {
return Promise.reject(`opt.type need to set 'function'`)
}
//runin
let runin = _.get(opt, 'runin', null)
if (runin !== 'both' && runin !== 'nodejs' && runin !== 'browser') {
runin = 'both'
}
//bLog
let bLog = _.get(opt, 'bLog', null)
if (!w.isbol(bLog)) {
bLog = true
}
//console
if (bLog) {
console.log('transpiling: ' + w.getFileName(fpSrc))
}
//輔助函數
let fpWpfPmSeries = `./src/wpf_pmSeries.umd.js`
let fpWpf = `./src/wpf.umd.js`
//id
let id = w.genID()
let fpTempParforCoreSrc = `./temp-${id}-parforCore-src.mjs`
let fpTempParforCoreTar = `./temp-${id}-parforCore-tar.wk.umd.js`
let fpTempParforSrc = `./temp-${id}-parfor-src.mjs`
async function core() {
//codeParforCore, 產生並儲存parforCore程式碼
let codeParforCore = genParforCoreCode(fpWpfPmSeries, fpSrc)
fs.writeFileSync(fpTempParforCoreSrc, codeParforCore, 'utf8')
//轉譯parforCore
await rollupWorker({
...opt, //沿用設定
name: 'parforCore', //一定要為parforCore, worker內會使用同名函數做繫節, 故不能更換, 除非改genParforCoreCode內的函數parforCore名稱
type: 'function', //原模組輸出為函數, 可傳入參數初始化
execFunctionByInstance: true, //原模組為計算函數回傳結果
fpSrc: fpTempParforCoreSrc,
fpTar: fpTempParforCoreTar,
// formatOut: 'es', //由外部決定
// bMinify: false, //由外部決定
bLog: false,
})
//codeParfor, 產生並儲存parfor程式碼
let codeParfor = genParforCode(fpWpf, fpTempParforCoreTar)
fs.writeFileSync(fpTempParforSrc, codeParfor, 'utf8')
///轉譯parfor
let codeRes = await rollupFile({
...opt, //沿用設定
fpSrc: fpTempParforSrc,
fdSrc: './',
// fdTar, //不給就是直接回傳code
hookNameDist: () => {
return name //指定轉譯名稱
},
bBanner: false,
bSourcemap: false, //rollupParfor不提供sourcemap
globals: { //因有已包含Nodejs與瀏覽器的worker封裝, 故需指定剔除Nodejs的worker的引用
'worker_threads': 'worker_threads',
// ...globals,
},
external: [
'worker_threads',
// ...external,
],
// formatOut: 'es', //由外部決定
// bMinify: false, //由外部決定
runin: runin === 'nodejs' ? 'nodejs' : 'browser', //parfor執行環境若為單獨指定沒問題, 若指定為both, 則強制視為運行於browser
bLog: false,
})
//writeFileSync
fs.writeFileSync(fpTar, codeRes, 'utf8')
//console
if (bLog) {
console.log('\x1b[32m%s\x1b[0m', 'output: ' + w.getFileName(fpTar))
}
}
//core
await core()
.catch((err) => {
console.log(err)
})
.finally(() => {
//unlinkSync, 不論轉譯成功失敗都刪除檔案
try {
fs.unlinkSync(fpTempParforCoreSrc)
}
catch (err) {
console.log(err)
}
try {
fs.unlinkSync(fpTempParforCoreTar)
}
catch (err) {
console.log(err)
}
try {
fs.unlinkSync(fpTempParforSrc)
}
catch (err) {
console.log(err)
}
})
}
export default rollupParfor