// import path from 'path'
import fs from 'fs'
import _ from 'lodash-es'
import w from './wsemip.umd.js'
// import rollupNodeWorker from './rollupNodeWorker.mjs'
// import rollupWebWorker from './rollupWebWorker.mjs'
import rollupWorkerCore from './rollupWorkerCore.mjs'
import rollupCode from './rollupCode.mjs'
function mergeNodejs(fpSrcNW) {
let c = `
import nw from '${fpSrcNW}'
export default nw
`
return c
}
function mergeBrowser(fpSrcWW) {
let c = `
import ww from '${fpSrcWW}'
export default ww
`
return c
}
function mergeBoth(fpSrcNW, fpSrcWW) {
let c = `
import nw from '${fpSrcNW}'
import ww from '${fpSrcWW}'
function isWindow() {
return typeof window !== 'undefined' && typeof window.document !== 'undefined'
}
let wk
if(isWindow()){
wk = ww
}
else {
wk = nw
}
export default wk
`
return c
}
function mergeCore(runin, fpSrcNW, fpSrcWW) {
if (runin === 'both') {
return mergeBoth(fpSrcNW, fpSrcWW)
}
else if (runin === 'nodejs') {
return mergeNodejs(fpSrcNW)
}
else if (runin === 'browser') {
return mergeBrowser(fpSrcWW)
}
throw new Error(`invalid runin[${runin}]`)
}
/**
* 使用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 rollupWorker(opt = {}) {
//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')
}
//fn
let fn = w.getFileName(fpSrc)
//fpTar
let fpTar = _.get(opt, 'fpTar', null)
if (!w.isestr(fpTar)) {
return Promise.reject('invalid opt.fpTar')
}
//nameDistType
let nameDistType = _.get(opt, 'nameDistType', null)
//nameDist
let nameDist = name
if (nameDistType === 'kebabCase') {
nameDist = _.kebabCase(name)
}
//hookNameDist
let hookNameDist = _.get(opt, 'hookNameDist', null)
if (_.isFunction(hookNameDist)) {
nameDist = hookNameDist(nameDist, name, fn)
}
//formatOut, umd為瀏覽器端直接使用, es為供vue-cli或webpack使用
let formatOut = _.get(opt, 'formatOut', null)
if (!formatOut) {
formatOut = 'es'
}
//targets
let targets = opt.targets
if (!w.isbol(targets)) {
targets = 'new' //於瀏覽器端,因程式碼用字串+blob方式作為web worker初始化之方式, 無法支援ie11(會需要改安全性)只好放棄, 且若被CSP檔那只能由伺服器改設定, 故此處直接改用最新語法new打包
}
//runin
let runin = _.get(opt, 'runin', null)
if (runin !== 'both' && runin !== 'nodejs' && runin !== 'browser') {
runin = 'both'
}
//bMinify
let bMinify = _.get(opt, 'bMinify', null)
if (!w.isbol(bMinify)) {
bMinify = true
}
//keepFnames
let keepFnames = _.get(opt, 'keepFnames', null)
if (!w.isbol(keepFnames)) {
keepFnames = false
}
//mangleReserved
let mangleReserved = _.get(opt, 'mangleReserved', null)
if (!w.isarr(mangleReserved)) {
mangleReserved = [] //可禁止使用'$', 因有些技術使用取代字串成轉譯後程式碼, 若轉譯後程式碼內含$&會導致觸發regex的插入匹配的字串, 從而造成非預期問題
}
//mainFields
let mainFields = _.get(opt, 'mainFields', null)
if (!w.isearr(mainFields)) {
mainFields = null
}
//globals, 提供字串需解析成物件, 指定內外模組的關聯性,左邊key為內部使用之模組名稱,右邊value為外部提供之模組名稱
let globals = _.get(opt, 'globals', null)
if (!w.isobj(globals)) {
globals = {}
}
//external, 提供字串需解析成陣列, 指定哪些內部模組需引用外部模組
let external = _.get(opt, 'external', null)
if (!w.isarr(external)) {
external = []
}
//bLog
let bLog = _.get(opt, 'bLog', null)
if (!w.isbol(bLog)) {
bLog = true
}
//console
if (bLog) {
console.log('transpiling: ' + w.getFileName(fpSrc))
}
//id
let id = w.genID()
let fpSrcNW = `./temp-${id}-nw.js`
let fpSrcWW = `./temp-${id}-ww.js`
let fpSrcMg = `./temp-${id}-mg.js`
async function core() {
//rollupWorkerCore for nodejs worker
let codeNW = ''
if (runin === 'both' || runin === 'nodejs') {
codeNW = await rollupWorkerCore({
...opt, //沿用設定
runin: 'nodejs',
bReturnCode: true,
bLog: false,
})
fs.writeFileSync(fpSrcNW, codeNW, 'utf8')
}
//rollupWorkerCore for browser web worker
let codeWW = ''
if (runin === 'both' || runin === 'browser') {
codeWW = await rollupWorkerCore({
...opt, //沿用設定
runin: 'browser',
bReturnCode: true,
bLog: false,
})
fs.writeFileSync(fpSrcWW, codeWW, 'utf8')
}
//mergeCore, 合併nodejs與web的worker的程式碼
let codeMerge = mergeCore(runin, fpSrcNW, fpSrcWW)
fs.writeFileSync(fpSrcMg, codeMerge, 'utf8')
// fs.writeFileSync(`./z-2-node[both]-1-mergeBoth.js`, codeMerge, 'utf8')
//rollupCode, 轉譯合併nodejs與web的worker的程式碼
let codeRes = await rollupCode(codeMerge, {
//不沿用設定
name: nameDist,
formatOut,
targets,
bSourcemap: false, //rollupCode不提供sourcemap
bBanner: false, //rollupCode不提供banner
runin: runin === 'nodejs' ? 'nodejs' : 'browser', //outer執行環境若為單獨指定沒問題, 若指定為both, 則強制視為運行於browser
bNodePolyfill: false, //outer不需使用node polyfill
bMinify,
keepFnames,
mangleReserved,
mainFields: null, //outer不需使用mainFields
globals: { //因有已包含Nodejs與瀏覽器的worker封裝, 故需指定剔除Nodejs的inner的worker的引用即可
'worker_threads': 'worker_threads',
...globals,
},
external: [
'worker_threads',
...external,
],
bLog: false,
})
//writeFileSync
fs.writeFileSync(fpTar, codeRes, 'utf8')
// fs.writeFileSync(`./z-2-node[both]-2-finall.js`, codeMerge, '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, 不論轉譯成功失敗都刪除檔案
if (runin === 'both' || runin === 'nodejs') {
try {
fs.unlinkSync(fpSrcNW)
}
catch (err) {
console.log(err)
}
}
if (runin === 'both' || runin === 'browser') {
try {
fs.unlinkSync(fpSrcWW)
}
catch (err) {
console.log(err)
}
}
try {
fs.unlinkSync(fpSrcMg)
}
catch (err) {
console.log(err)
}
})
}
export default rollupWorker