I am trying to compile a library that I've written so that the distributed file can be put in a script tag and run in the browser. I am trying to do this with Parcel 2. I feel like I'm so close, but each time I think I'm there some new problem occurs.
One key thing is that I'd like it not to bundle external dependencies (e.g. jQuery) because they may already be present in sites that will use this library.
The goal is to be able to write an HTML file which contains the following, and have everything load and work correctly
<script src="/jquery.js"></script>
<script src="/mylib.js"></script>
The Parcel regular build (e.g. parcel build src/mylib.js) outputs the compiled JS as CommonJS, which means that it has require() functions in there. These do not work in the browser.
One option I'm looking at is RequireJS or Browserify to then make that CommonJS work, but I'd really like the Parcel build to just output what I need.
In package.json:
"targets" : {
"regular" : {
"source": "src/mylib.js",
"context" : "browser",
"outputFormat" : "global",
"isLibrary" : false,
"includeNodeModules" : {
"jquery": false
}
}
}
context and outputFormat attempt to force pure client-side javascript output.
On build, this outputs:
@parcel/packager-js: External modules are not supported when building for browser
I added the includeNodeModules.jQuery=false in the hope of side-stepping this issue, but it did not work.
Using parcel serve view/myapp.html builds a version of the app which absolutely runs in the browser. require() is replaced by some Parcel internals (newRequire or parcelRequires I think) and it works exactly like you would want. Except that it also bundles a load of websockets stuff to integrate with the Parcel built-in server, so it can't be distributed for usage. But it's totally clear that Parcel can build client-side only JavaScript, I just can't figure out what parcel serve is doing under the hood to get the parcel build command to work in the same way.
This approach also bundles externals (i.e. jQuery) which I don't want it to do, but if I could at least get a distributable output that didn't have all the websockets/dev stuff in it that would be a step in the right direction.
What am I missing? Is what I want to do possible with Parcel, or am I going to then need to convert CommonJS to Browser JS?
Thank you!
A little more experimentation has yielded another step forward to a dead end:
in package.json I have specified a target for a production build that excludes jquery:
"targets" : {
"demo" : {
"source" : "views/demo.html",
"includeNodeModules" : {
"jquery" : false
}
}
},
This builds something that would run in the browser except that it is prefixed with
import t from"jquery"
If I set the script tag to type="module" then this will load, but it is unable to resolve jquery to the version of jquery that I have specified in a separate script tag. Perhaps there is a way to tell a script module that it's "name" is jquery?
This still isn't quite what I would want, as I may not control the jquery script tag that this library would use, I just want it to accept that there would be a $ in the global namespace that it could use.
Instead of trying to exclude jquery from an "outputFormat": "global" bundle using includedNodeModules try using global aliases instead:
Your package.json might look like this:
{
...
"global": "dist/library.global.js",
"targets": {
"global": {
"context": "browser",
"outputFormat": "global"
}
},
"alias": {
"jquery": {
"global": "$"
}
},
"scripts": {
"watch": "parcel watch src/library.js",
"build": "parcel build src/library.js",
},
"peerDependencies": {
"jquery": "^3.6.0"
},
"devDependencies": {
"parcel": "2.0.0-rc.0",
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With