import fs from 'fs'
import _ from 'lodash-es'
import w from './wsemip.umd.js'
import rollupFile from './rollupFile.mjs'
import rollupCode from './rollupCode.mjs'
let cmain = 'main' //'__system__main__'
function str2b64(str) {
return Buffer.from(str, 'utf8').toString('base64')
}
function clearExportCode(code, name) {
//clec, cjs會轉譯成為「module.exports = OOO;」
let clec = () => {
let err = ''
let c = `module.exports = ${name};`
let s = _.split(code, c)
if (_.size(s) < 2) {
err = `找不到 '${c}'`
return { err }
}
else if (_.size(s) > 2) {
err = `出現多個 '${c}'`
return { err }
}
return _.get(s, 0)
}
//clecm, cjs會轉譯與壓縮成為「module.exports=OOO;」
let clecm = () => {
let err = ''
let c = `module.exports=${name};`
let s = _.split(code, c)
if (_.size(s) < 2) {
err = `找不到 '${c}'`
return { err }
}
else if (_.size(s) > 2) {
err = `出現多個 '${c}'`
return { err }
}
return _.get(s, 0)
}
//cleim, cjs會轉譯與壓縮成為「module.exports=程式碼」, 因同樣會出現module.exports, 故須等前面cjs兩種都偵測完再偵測
let cleim = () => {
let c = `module.exports=`
if (code.indexOf(c) >= 0) {
code = code.replace(`module.exports=`, `let ${name}=`)
return code
}
}
//cled, es6會轉譯成為「export default OOO;」
let cled = () => {
let err = ''
let c = `export default ${name};`
let s = _.split(code, c)
if (_.size(s) < 2) {
err = `找不到 '${c}'`
return { err }
}
else if (_.size(s) > 2) {
err = `出現多個 '${c}'`
return { err }
}
return _.get(s, 0)
}
//clead, es6有專案設定導致轉譯成為「export { OOO as default };」
let clead = () => {
let err = ''
let c = `export { ${name} as default };`
let s = _.split(code, c)
if (_.size(s) < 2) {
err = `找不到 '${c}'`
return { err }
}
else if (_.size(s) > 2) {
err = `出現多個 '${c}'`
return { err }
}
return _.get(s, 0)
}
//cleadm, es6有專案設定導致轉譯與壓縮成為「export{OOO as default};」
let cleadm = () => {
let err = ''
let c = `export{${name} as default};`
let s = _.split(code, c)
if (_.size(s) < 2) {
err = `找不到 '${c}'`
return { err }
}
else if (_.size(s) > 2) {
err = `出現多個 '${c}'`
return { err }
}
return _.get(s, 0)
}
//clec
let rec = clec()
//check
if (!w.isestr(_.get(rec, 'err'))) {
return rec
}
//clecm
let recm = clecm()
//check
if (!w.isestr(_.get(recm, 'err'))) {
return recm
}
//cleim
let reim = cleim()
//check
if (!w.isestr(_.get(reim, 'err'))) {
return reim
}
//cled
let red = cled()
//check
if (!w.isestr(_.get(red, 'err'))) {
return red
}
//clead
let read = clead()
//check
if (!w.isestr(_.get(read, 'err'))) {
return read
}
//cleadm
let readm = cleadm()
//check
if (!w.isestr(_.get(readm, 'err'))) {
return readm
}
//err
console.log(red.err, read.err, readm.err)
throw new Error('can not clear export default')
}
function genInnerWebWorkerCodeEv(evName) {
let c = `
r.on('${evName}',(msg) => {
//sendMessage
let res = {
mode: 'emit',
evName: '${evName}',
msg,
}
sendMessage(res)
})
`
return c
}
function genInnerWebWorkerCodeEvs(evNames) {
let c = ''
for (let i = 0; i < evNames.length; i++) {
let evName = evNames[i]
c += genInnerWebWorkerCodeEv(evName)
}
return c
}
function addInnerWebWorkerCode(code, name, evNames, opt = {}) {
//evs
let evs = genInnerWebWorkerCodeEvs(evNames)
//cnwt
let cnwt = ''
if (opt.runin === 'nodejs') {
cnwt = `
//import { parentPort } from 'worker_threads'
let { parentPort } = require('worker_threads') //因package.json不給type=module故無法支援es6 import, 得使用require
//若要於nodejs worker內使用無法轉譯的原生套件例如fs, 避免使用頂層import加載使用, 因無法轉譯會直接保留
//並因import位於worker外層限定為require區(package.json不給type=module), 故出現錯誤無法轉譯
`
}
//csm
let csm = ''
if (opt.runin === 'nodejs') {
csm = `
parentPort.postMessage(data)
`
}
else {
csm = `
self.postMessage(data)
`
}
//ciit
let ciit = ''
if (opt.type === 'function' && opt.execFunctionByInstance) {
ciit = `
r = {
${cmain}: ${name}
}
`
}
else {
ciit = `
r = ${name}(...input)
`
}
//crm
let crm = ''
if (opt.runin === 'nodejs') {
crm = `
parentPort.on('message', recvMessage)
`
}
else {
crm = `
self.onmessage = function (e) {
recvMessage(e.data)
}
`
}
let c = `
${cnwt}
${code}
let instance = null
function init(input){
//init
let r
${ciit}
//on
${evs}
//save
instance = r
}
function sendMessage(data) {
${csm}
}
async function run(data) {
// console.log('inner worker run',data)
//mode
let mode = data.mode
//check
if(mode !== 'init' && mode !== 'call'){
return
}
//init
if(mode === 'init'){
try{
//type
let type = data.type
//input
let input = data.input
//instance
if(type === 'function'){
init(...input)
}
else if(type === 'object'){
instance = ${name}
}
}
catch(err){
//sendMessage
let res = {
mode: 'emit',
evName: 'error',
msg: err,
}
sendMessage(res)
}
}
//check
if(mode === 'call'){
let state = ''
let msg = null
try{
//fun
let fun = instance[data.fun]
//input
let input = data.input
//exec
await fun(...input)
.then((suc) => {
state='success'
msg=suc
})
.catch((err) => {
state='error'
msg=err
})
}
catch(err){
state = 'error'
msg = err
}
//sendMessage
let res = {
mode: 'return',
id: data.id,
fun: data.fun,
state,
msg,
}
sendMessage(res)
}
}
function recvMessage(data) {
// console.log('inner worker recv:', data)
//dataRecv
let dataRecv = data
//run
run(dataRecv)
}
${crm}
try{
//僅以uncaughtExceptionMonitor記錄, 不可註冊uncaughtException與unhandledRejection監聽:
//於worker內註冊此二監聽會阻止Node預設的worker終止行為, 使worker崩潰後仍存活空轉,
//外層收不到error/exit事件, pending promise永久懸置, 且存活殭屍worker會令宿主程序無法退出;
//移除後worker依預設行為死亡, error事件傳至外層, 由外層整批reject pending
// process.on('unhandledRejection', (err) => {
// console.log('inner:unhandledRejection', err)
// })
// process.on('uncaughtException', (err) => {
// console.log('inner:uncaughtException', err)
// })
process.on('uncaughtExceptionMonitor', (err) => {
console.log('inner:uncaughtExceptionMonitor', err)
})
}
catch(err){}
`
return c
}
function genOuterWebWorkerCodeFun(funName) {
let c = `
function ${funName}(){
//pm
let pm = genPm()
//check, worker已死(崩潰或異常退出)後之呼叫立即reject, 不得懸置
if (bDead) {
pm.reject('worker is dead')
return pm
}
//id
let id = genID()
//pendings, 登記pending promise, 供worker崩潰或異常退出時整批reject
pendings[id] = pm
//dataSend
let dataSend = {
mode:'call',
id,
fun: '${funName}',
input: [...arguments], //若直接用arguments會無法轉譯
}
//postMessage
wk.postMessage(dataSend)
//once
ev.once(id, (res) => {
delete pendings[id]
if (res.state === 'success') {
pm.resolve(res.msg)
}
else {
pm.reject(res.msg)
}
})
return pm
}
`
return c
}
function genOuterWebWorkerCodeFuns(funNames) {
let c = ''
for (let i = 0; i < funNames.length; i++) {
let funName = funNames[i]
c += genOuterWebWorkerCodeFun(funName)
}
return c
}
function genOuterWebWorkerCodeInstanceObj(funName) {
let c = `
${funName}: async function(){
let input = [...arguments]
let nww = wrapWorker()
let r = await nww.${funName}(...input)
.finally(() => {
nww.terminate() //每次執行完不論成功失敗都要中止worker
})
return r
},
`
return c
}
function genOuterWebWorkerCodeInstanceObjs(funNames) {
let c = ''
for (let i = 0; i < funNames.length; i++) {
let funName = funNames[i]
c += genOuterWebWorkerCodeInstanceObj(funName)
}
return c
}
function addOuterWebWorkerCode(code, funNames, opt = {}) {
// //codeShow
// let codeShow = w.replace(code, '`', '\'')
//codeB64
let codeB64 = str2b64(code)
// let codeB64 = code
//cipwt
let cipwt = ''
if (opt.runin === 'nodejs') {
cipwt = `
import { Worker } from 'worker_threads'
`
}
else {
cipwt = `
import { Base64 } from 'js-base64'
`
}
//cb642str
let cb642str = ''
if (opt.runin === 'nodejs') {
cb642str = `
//return b64
return Buffer.from(b64, 'base64').toString('utf8') //Nodejs端使用Buffer解碼
`
}
else {
cb642str = `
//return b64
return Base64.decode(b64) //瀏覽器端使用Base64解碼
// return window.atob(b64) //瀏覽器端執行使用 atob 或 decodeURIComponent(atob()) 或 unescape(decodeURIComponent(atob('ooxx'))) 無法解Buffer轉出的b64, 因這只能解由瀏覽器對應的函數產生b64
`
}
//modify funNames for execFunctionByInstance
if (opt.type === 'function' && opt.execFunctionByInstance) {
funNames = [cmain]
}
//genOuterWebWorkerCodeFuns
let cfs = genOuterWebWorkerCodeFuns(funNames)
//cev
let cev = _.join(_.map(funNames, (funName) => {
return `ev.${funName} = ${funName}`
}), '\n')
//cnw
let cnw = ''
if (opt.runin === 'nodejs') {
cnw = `
return new Worker(code, { eval: true })
`
}
else {
cnw = `
let blob = new Blob([code]) //blob for Chrome 8+, Firefox 6+, Safari 6.0+, Opera 15+
let URL = window.URL || window.webkitURL
return new Worker(URL.createObjectURL(blob))
`
}
//cwt
let cwt = ''
if (opt.type === 'function') {
if (opt.execFunctionByInstance) {
cwt = `
ww = async function (){
let input = [...arguments]
let nww = wrapWorker()
let r = await nww.main(...input) //nww.main需跟cmain一致
.finally(() => {
nww.terminate() //每次執行完不論成功失敗都要中止worker
})
return r
}
`
}
else {
cwt = `
ww = wrapWorker
`
}
}
else {
if (opt.execObjectFunsByInstance) {
cwt = `
let funs = {
${genOuterWebWorkerCodeInstanceObjs(funNames)}
}
ww = evem()
for(let k in funs){
let v = funs[k]
ww[k] = v
}
`
}
else {
cwt = `
ww = wrapWorker()
`
}
}
//cee, worker之error屬致命錯誤(worker隨後死亡), 用emitFatal標記死亡並整批reject pending
let cee = ''
if (opt.runin === 'nodejs') {
cee = `
wk.on('error', emitFatal)
`
}
else {
cee = `
wk.onerror = emitFatal
`
}
//cte
let cte = ''
if (opt.runin === 'nodejs') {
cte = `
wk.on('exit', (code) => {
//worker停止(崩潰後exit code 1, 或worker內process.exit之任意code): 凡非由terminate()主動中止一律視為異常,
//標記死亡並整批reject pending; 崩潰時error事件先行已標記死亡, 此處emitFatal因bDead為true而不重複發送
if (!bTerminated) {
emitFatal('worker exit, code['+code+']')
}
})
`
}
else {
cte = `
wk.onmessageerror = function (e) {
emitError(e.data)
}
`
}
//crm
let crm = ''
if (opt.runin === 'nodejs') {
crm = `
wk.on('message', recvMessage)
`
}
else {
crm = `
wk.onmessage = function (e) {
recvMessage(e.data)
}
`
}
//cmn
let cmn = ''
if (opt.type === 'function' && opt.execFunctionByInstance) {
cmn = `
ev.${cmain} = ${cmain}
`
}
let c = `
${cipwt}
import EventEmitter from 'eventemitter3'
function isWindow() {
return typeof window !== 'undefined' && typeof window.document !== 'undefined'
}
//ww
let ww
function protectShell() {
//cEnv
let cEnv = isWindow()?'browser':'nodejs'
//check, 後續會有Nodejs或瀏覽器依賴的API例如window.atob或Buffer, 於import階段時就先行偵測跳出
if(cEnv !== '${opt.runin}'){
return null
}
function evem() {
return new EventEmitter()
}
function genPm() {
let resolve
let reject
let p = new Promise(function() {
resolve = arguments[0]
reject = arguments[1]
})
p.resolve = resolve
p.reject = reject
return p
}
function genID(len = 10) {
let uuid = []
let chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('')
let radix = chars.length
for (let i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]
let r = uuid.join('')
return r
}
function b642str(b64) {
${cb642str}
}
//codeShow
//codeB64, 此處需提供worker執行程式碼, 因有特殊符號轉譯困難, 故需先轉base64再使用
let codeB64 = \`${codeB64}\`
//code
let code = b642str(codeB64)
function wrapWorker() {
//evem
let ev = evem()
//pendings, 呼叫中promise登記表; bDead, worker已死標記(死後呼叫立即reject); bTerminated, 由terminate()主動中止標記(exit事件不視為異常)
let pendings = {}
let bDead = false
let bTerminated = false
function emitError(err) {
//先整批reject pending: 錯誤發生時呼叫端不得永久懸置(pending promise之settle僅靠worker回傳return訊息, worker崩潰即永無回音)
for (let id in pendings) {
let pm = pendings[id]
delete pendings[id]
ev.removeAllListeners(id) //清除genOuterWebWorkerCodeFun內對應once監聽, 避免監聽器殘留
pm.reject(err)
}
ev.emit('error', err)
}
function emitFatal(err) {
//致命錯誤: worker已無法再服務(崩潰,異常退出,建立失敗), 標記死亡使後續呼叫立即reject; 已標記則不重複發送
if (bDead) {
return
}
bDead = true
emitError(err)
}
function genWorker(code) {
//new Worker
try {
${cnw}
}
catch (err) {
emitFatal(err)
}
}
//genWorker
let wk = genWorker(code)
//check, 於瀏覽器端可能會遭遇IE11安全性問題, 或被CSP的worker-src或script-src設定阻擋
if (!wk) {
emitFatal('invalid worker')
return null
}
function terminate() {
if (wk) {
bTerminated = true //先標記再中止, 使exit事件不被視為異常
wk.terminate()
wk = undefined
}
else {
emitError('worker has been terminated')
}
}
function init(){
//dataSend
let dataSend = {
mode:'init',
type:'${opt.type}',
input: [...arguments], //若直接用arguments會無法轉譯
}
//postMessage
wk.postMessage(dataSend)
}
${cfs}
function recvMessage(data) {
// console.log('outer worker recv:', data)
//dataRecv
let dataRecv = data
//mode
let mode = dataRecv.mode
//check
if(mode !== 'emit' && mode !== 'return'){
return
}
//emit
if(mode === 'emit'){
//emit
ev.emit(dataRecv.evName, dataRecv.msg)
}
//return
if(mode === 'return'){
//emit
ev.emit(dataRecv.id, dataRecv)
}
}
//bind recvMessage
${crm}
//bind emitFatal
${cee}
//bind emitFatal(nodejs exit)或emitError(browser messageerror) for special condition
${cte}
//init
init([...arguments]) //若直接用arguments會無法轉譯
${cev}
${cmn}
ev.terminate = terminate
return ev
}
//set ww
${cwt}
}
protectShell()
try{
//此段為outer層程式碼, 運行於引用打包產物之宿主(app)主程序, 不可註冊uncaughtException與unhandledRejection監聽:
//註冊後宿主整個程序的未捕捉錯誤都會被改成[只印log不終止], 覆蓋Node預設的崩潰行為,
//等於函式庫劫持了所有下游app的全域錯誤語意, 錯誤被靜默吞掉難以察覺;
//與inner層worker內移除同名監聽屬同族修正(inner層吞噬會阻止worker死亡, 使外層收不到error/exit而令pending懸置),
//故僅保留uncaughtExceptionMonitor, 其只觀測記錄, 不影響預設終止行為
// process.on('unhandledRejection', (err) => {
// console.log('outer:unhandledRejection', err)
// })
// process.on('uncaughtException', (err) => {
// console.log('outer:uncaughtException', err)
// })
process.on('uncaughtExceptionMonitor', (err) => {
console.log('outer:uncaughtExceptionMonitor', err)
})
}
catch(err){}
export default ww
`
return c
}
/**
* 使用rollup轉譯檔案,並封裝至前端web worker內或後端nodejs 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 {Boolean} [opt.execFunctionByInstance=true] 輸入若模組類型為物件type='function'時,是否將function視為使用獨立實體執行並自動銷毀實體布林值,例如原模組就是一個運算函數,不需要回傳eventemmitter監聽事件,預設true
* @param {Boolean} [opt.execObjectFunsByInstance=true] 輸入若模組類型為物件type='object'時,各函式是否使用獨立實體執行布林值,例如使用到stream的各函式會因共用同一個實體導致降速,故各函數需自動有各自實體,預設true
* @param {Array} [opt.funNames=[]] 輸入模組可被呼叫的函數名稱陣列,預設[]
* @param {Array} [opt.evNames=[]] 輸入模組可監聽的函數名稱陣列,預設[]
* @param {String} opt.fpSrc 輸入原始碼檔案位置字串
* @param {Boolean} [opt.bReturnCode=false] 輸入是否回傳轉譯後程式碼而不輸出布林值,設定為true時則fpTar失效,預設false
* @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='browser'] 輸入執行環境字串,可選'nodejs'、'browser',預設'browser'
* @param {Boolean} [opt.bNodePolyfill=false] 輸入轉譯是否自動加入Nodejs polyfill布林值,主要把Nodejs語法(例如fs)轉為瀏覽器端語法,預設false
* @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 rollupWorkerCore(opt = {}) {
let rpOpt
//name
let name = _.get(opt, 'name', null)
if (!w.isestr(name)) {
return Promise.reject('invalid opt.name')
}
//type
let type = _.get(opt, 'type', null)
if (type !== 'function' && type !== 'object') {
type = 'object'
}
//execFunctionByInstance
let execFunctionByInstance = _.get(opt, 'execFunctionByInstance', null)
if (!w.isbol(execFunctionByInstance)) {
execFunctionByInstance = true
}
//execObjectFunsByInstance
let execObjectFunsByInstance = _.get(opt, 'execObjectFunsByInstance', null)
if (!w.isbol(execObjectFunsByInstance)) {
execObjectFunsByInstance = true
}
//funNames
let funNames = _.get(opt, 'funNames', null)
if (!w.isearr(funNames)) {
if (type === 'function' && execFunctionByInstance) {
funNames = []
}
else {
return Promise.reject('invalid opt.funNames')
}
}
//evNames, 可不給予監聽事件
let evNames = _.get(opt, 'evNames', null)
if (!w.isarr(evNames)) {
evNames = []
}
//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)
//bReturnCode
let bReturnCode = _.get(opt, 'bReturnCode', null)
if (!w.isbol(bReturnCode)) {
bReturnCode = false
}
//fpTar
let fpTar = _.get(opt, 'fpTar', null)
if (!bReturnCode && !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可支援nodejs或為瀏覽器端直接使用, 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 !== 'nodejs' && runin !== 'browser') {
console.log(`invalid runin[${runin}], set to 'browser'`)
runin = 'browser'
}
//bNodePolyfill
let bNodePolyfill = _.get(opt, 'bNodePolyfill', null)
if (!w.isbol(bNodePolyfill)) {
bNodePolyfill = false
}
//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 = []
}
//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))
}
//rollupFile, 預處理, 把code內的關聯都打包出來, 故需用es, 程式碼之後還會轉譯故targets使用new, 此處要用rollupFile對原檔案打包, 才能正確引入相關模組與套件
rpOpt = {
//不沿用設定
// name, //打包成cjs不需要name
fn,
fdSrc: w.getPathParent(fpSrc),
// fdTar: '', //沒給代表回傳程式碼
format: 'cjs', //rollupFile須使用format
targets,
bSourcemap: false, //預設值為true得關閉
bBanner: false,
runin,
bNodePolyfill,
bMinify,
keepFnames,
mangleReserved: [name, ...mangleReserved],
mainFields,
globals: {},
external: [],
bLog: false,
}
if (runin === 'nodejs') {
rpOpt.globals = { //需指定剔除Nodejs的inner的worker的引用
'worker_threads': 'worker_threads',
}
rpOpt.external = [
'worker_threads',
]
}
rpOpt.globals = {
...rpOpt.globals,
...globals,
}
rpOpt.external = [
...rpOpt.external,
...external,
]
let codeTransOri = await rollupFile(rpOpt)
// fs.writeFileSync(`./z-1-node[${runin}]-1-codeTransOri.js`, codeTransOri, 'utf8')
//clearExportCode
let codeTransClr = clearExportCode(codeTransOri, name)
// fs.writeFileSync(`./z-1-node[${runin}]-2-codeTransClr.js`, codeTransClr, 'utf8')
//addInnerWebWorkerCode
let codeTransAdd = addInnerWebWorkerCode(codeTransClr, name, evNames, { runin, type, execFunctionByInstance, execObjectFunsByInstance })
// fs.writeFileSync(`./z-1-node[${runin}]-3-codeTransAdd.js`, codeTransAdd, 'utf8')
//addOuterWebWorkerCode
let codeMerge = addOuterWebWorkerCode(codeTransAdd, funNames, { runin, type, execFunctionByInstance, execObjectFunsByInstance })
// fs.writeFileSync(`./z-1-node[${runin}]-4-codeMerge.js`, codeMerge, 'utf8')
//rollupCode, 轉譯合併內外worker的程式碼
rpOpt = {
//不沿用設定
name: nameDist,
formatOut, //rollupCode使用formatIn(可不給)與formatOut
targets,
bSourcemap: false, //rollupCode不提供sourcemap
bBanner: false, //rollupCode不提供banner
runin,
bNodePolyfill: false, //outer不需使用node polyfill
bMinify,
mainFields: null, //outer不需使用mainFields
globals: {},
external: [],
bLog: false,
}
if (runin === 'nodejs') {
rpOpt.globals = { //需指定剔除Nodejs的inner的worker的引用
'worker_threads': 'worker_threads',
}
rpOpt.external = [
'worker_threads',
]
}
rpOpt.globals = {
...rpOpt.globals,
...globals,
}
rpOpt.external = [
...rpOpt.external,
...external,
]
let codeRes = await rollupCode(codeMerge, rpOpt)
// fs.writeFileSync(`./z-1-node[${runin}]-5-codeRes.js`, codeRes, 'utf8')
if (bReturnCode) {
return codeRes
}
//writeFileSync
fs.writeFileSync(fpTar, codeRes, 'utf8')
//console
if (bLog) {
console.log('\x1b[32m%s\x1b[0m', 'output: ' + w.getFileName(fpTar))
}
}
export default rollupWorkerCore