Parcel.js is a “Blazing fast, zero configuration web application bundler.” In this post, we’re going to take an ASP.NET Core website template that uses Bootstrap 4 and set it up to use Parcel-generated bundles instead.
ASP.NET Core supports bundling and minifying static assets at design-time using the community supported BuildBundlerMinifier package that can be configured in a bundleconfig.json file. However it’s not well suited for scenarios that would benefit from a deploy-time bundling strategy, i.e. assets are built during deployment and output files are not checked in.
This is where Parcel.js comes in. Parcel is a “Blazing fast, zero configuration web application bundler.” The zero-configuration bit is its major selling point because it allows you to get started with minimal effort.
In this post, we’re going to take an ASP.NET website template that uses Bootstrap 4 and set it up to use Parcel-generated bundles instead.
dotnet new webapp --name AspNetParcelExp cd AspNetParcelExp
{
"name": "aspnet-parcel-exp",
"private": true,
"version": "0.1.0"
}
npm install --save-dev parcel-bundler@1
npm install jquery@3
npm install popper.js@1
npm install bootstrap@4
npm install jquery-validation@1
npm install jquery-validation-unobtrusive@3
If everything went right, your package.json should look something like this:
{
"name": "aspnet-parcel-exp",
"private": true,
"version": "0.1.0",
"devDependencies": {
"parcel-bundler": "^1.11.0"
},
"dependencies": {
"bootstrap": "^4.2.1",
"jquery": "^3.3.1",
"jquery-validation": "^1.19.0",
"jquery-validation-unobtrusive": "^3.2.11",
"popper.js": "^1.14.7"
}
}
/AspNetParcelExp/ # project root
- .sassrc # sass configuration
- assets/ # front end assets root
- scss/ # Place for all styles
- site.scss
- js/ # Place for all scripts
- site.js
- bundle.js # Entry point for our output bundle
// Import styles
import './scss/site.scss'
// Setup jquery
import $ from 'jquery'
window.$ = window.jQuery = $
// Import other scripts
import 'bootstrap'
import 'jquery-validation'
import 'jquery-validation-unobtrusive'
import './js/site'
We import everything we depend on. ‘bootstrap’ for example refers to the …/node_modules/bootstrap/ folder. If you want to import a specific file from a package only, you may do that too. The above code should be straightforward, except for maybe jQuery, which I’ll explain in a bit.
{
"includePaths": [
"./node_modules/"
]
}
This will allow referencing package folders without a full path to it. See parcel-bundler/parcel#39 for more information.
@import "~bootstrap/scss/bootstrap";
You may also just include the bootstrap SCSS files that you actually need to keep the output size down. Since we’re trying to replicate the template, we could also paste the code in the original template’s site.css here after the line.
Since we have no global scripts, we leave the site.js file empty for now.
Add a scripts block to the package.json file right before the devDependencies: { line:
"scripts": {
"build": "parcel build assets/bundle.js --out-dir wwwroot/dist/",
"watch": "parcel watch assets/bundle.js --out-dir wwwroot/dist/"
},
This adds scripts that can be invoked as npm run build to build, for example. It passes the bundle.js entry point to Parcel, and instructs it to generate output files in the wwwroot/dist/ using the --out-dir option.
npm run build
You should now see a bundle.css, bundle.js and a bundle.map file in the wwwroot/dist directory (the directory we specified for the build script above). It’s a good idea to ignore the wwwroot/dist from version control.
<script src="~/dist/bundle.js"
asp-append-version="true"></script>
And replace the stylesheet <link> tags with:
<link rel="stylesheet"
href="~/dist/bundle.css"
asp-append-version="true" />
That’s it. If you did everything right, running the program should display the same output as with the old files.
If it feels like a lot of work, it’s probably because you aren’t familiar with the npm, SCSS, etc., so take your time.
Rather than running npm run build each time you make changes, you can use HMR (Hot Module Replacement), which will detect pages and reload for you, so that you don’t have to do it.
Open a new terminal instance and run npm run watch. Keep this running while performing any dev changes — it’ll speed you up.
Add the following to the AspNetParcelExp.csproj file right before the closing </Project> tag:
<Target Name="ParcelBeforePublish"
BeforeTargets="PrepareForPublish">
<Exec Command="npm run build" />
</Target>
Now, every time you create a publish package, it will run the npm build script. This is particularly important in Continuous Delivery scenarios, because the wwwroot/dist is (usually) not under version control, and the build environment needs to build the files before deploying. You may test this step using dotnet publish: you’ll see output from parcel-bundler.
If you want the task to be run every time is the project is built, change PrepareForPublish to BeforeBuild.
The parcel-bundler generates a CommonJS module, which means it doesn’t pollute the global window object. Now this can be a problem sometimes, because some libraries — particularly the old ones — have always been polluting window.
Take jQuery for instance. Libraries that require jQuery perform a test on the window object to check if it’s got a jQuery or a $ property. Since CommonJS libraries don’t pollute window, these checks will fail. So we’ll need to manually pollute the window object ourselves. We did that for jquery in bundle.js using:
import $ from 'jquery'
window.$ = window.jQuery = $
This is one thing you need to remember when using Parcel.js or other similar bundlers.
{
"plugins": {
"autoprefixer": true
}
}
Galdin Raphael is an independent software developer. When not writing code, he's probably reading a book, playing the piano or studying music theory. Follow him on
twitter: @gldraphael