You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Here's an idea to solve some general dynamic / conditional requiring issues.
The current common issues are when the code is hard for a static analyzer we are using to parse, the required modules will not be bundled into the target file. E.g.
// ./main.jsconstpaths=['./a.js','./b/js','./c.js']paths.forEach(p=>{require(p);// this won't be analyzed correctly with our current webpack})
To solve this in my project, I hijacked apis like fs.readdirfs.readfilerequireglob etc. with a "pre-loader" to load these files (i.e. run the project to make "dynamic" works) before using ncc. All the dynamic file paths will be passed through the loader and put into a "bridge" file generated like this:
Then running ncc will go through the hijacked require's so that all the dynamic files will be bundled.
To make this more clear, here is a simple demo implementation of this "bundle helper":
import*asfsfrom'fs'/** * A bundle helper to make ncc happy */classBundleHelper{constructor(){this.initBridge()}publicIS_BUNDLING_MODE(){returnprocess.env.BUNDLING==='true'}publicIS_BUNDLED_MODE(){returnprocess.env.BUNDLED==='true'}/** * registers a file to require when bundling * @param filePath file path to require * @param customKey you can also use this key to require when in bundled mode; default: filePath */publicregisterFile(filePath: string,customKey?: string){constkey=this.encodeFilePath(filePath)// always exports normal filePath so we can get a complete file listletdata=`export const ${key} = () => require('${filePath}')\n`if(customKey){data+=`export const ${customKey} = () => require('${filePath}')\n`}fs.appendFileSync('./bridge.ts',data)}/** * requires a file and cache it for bundling * @param filePath file path or (custom key if provided) to require * @param customKey you can also use this key to require when in bundled mode; default: filePath * @returns file content */publicrequire(filePath: string,customKey?: string){if(this.IS_BUNDLED_MODE()){constkey=customKey??this.encodeFilePath(filePath)returnthis.getBridgeContent()[key]()}elseif(this.IS_BUNDLING_MODE()){this.registerFile(filePath)returnrequire(filePath)}else{returnrequire(filePath)// normally just require}}/** * gets registered file list when bundled * this is helpful when your are using libraries like "glob" * @returns registered file list */publicgetFileList(){constexports=this.getBridgeContent()constfileList=Object.keys(exports).filter((k)=>k.includes('_slash_')).map((k)=>this.decodeFilePath(k))returnfileList}privategetBridgeContent(){returnrequire('./bridge')}privateencodeFilePath(filePath: string){returnfilePath.replace(/\//g,'_slash_').replace(/\./g,'_dot_')}privatedecodeFilePath(filePath: string){returnfilePath.replace(/_slash_/g,'/').replace(/_dot_/g,'.')}privateinitBridge(){if(!this.IS_BUNDLED_MODE())returnfs.writeFileSync('./bridge.ts','')}}exportdefaultnewBundleHelper()
To use it, replace all dynamic require's with bundleHelper.require or just hijack them. And run ncc using:
process.env.BUNDLING='true'// enable for bundlingmain()// start your app - preload for cachingexecSync('ncc main.ts -o output');// run ncc// don't forget prepend "process.env.BUNDLED = 'true'" to your output/main.js file.
I'm using this in a couple of my project so far. I think this could be more general and can be integrated into ncc to solve some common dynamic requiring cases.
Any ideas or suggestions?
The text was updated successfully, but these errors were encountered:
@songkeys moving the analysis down to this level is a nice idea, there are two main issues though:
Code coverage is hard to achieve. The "integration run" needs to cover all dynamic code paths which is difficult to achieve and also provide as a library feature at the same time.
Even with perfect code coverage there are potential dynamic paths that might never be taken unless some specific condition holds, which multi-path analysis is effectively better at capturing.
Achieving both of the above via build-time execution risks build system security and side effects, given the lack of a comprehensive virtualization enironment. In the end getting such an approach right is almost more of a service than a library...
Here's an idea to solve some general dynamic / conditional requiring issues.
The current common issues are when the code is hard for a static analyzer we are using to parse, the required modules will not be bundled into the target file. E.g.
To solve this in my project, I hijacked apis like
fs.readdir
fs.readfile
require
glob
etc. with a "pre-loader" to load these files (i.e. run the project to make "dynamic" works) before usingncc
. All the dynamic file paths will be passed through the loader and put into a "bridge" file generated like this:Then running
ncc
will go through the hijacked require's so that all the dynamic files will be bundled.To make this more clear, here is a simple demo implementation of this "bundle helper":
To use it, replace all dynamic
require
's withbundleHelper.require
or just hijack them. And run ncc using:I'm using this in a couple of my project so far. I think this could be more general and can be integrated into ncc to solve some common dynamic requiring cases.
Any ideas or suggestions?
The text was updated successfully, but these errors were encountered: