Industry_Dark_870x220

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.

Create & Set Up a New ASP.NET Project

  1. Create a web project that uses Razor Pages. To do this on the command line, run:
    dotnet new webapp --name AspNetParcelExp cd AspNetParcelExp
  2. Delete the folders under wwwroot. (You may delete this later on, if you want it for reference — our goal is to generate these files using Parcel and use those instead.)

Install npm Dependencies

  1. Add a package.json file to the project root like the following:
  2. {
      "name": "aspnet-parcel-exp",
      "private": true,
      "version": "0.1.0"
    }
    
  1. Add parcel-bundler as a dev dependency:
  2. npm install --save-dev parcel-bundler@1
  1. Install the libraries we deleted using npm:
  2. 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"
      }
    }
    

Set Up an Asset Bundle Using Parcel.js

  1. Under the project root, create files with the following structure:
  2.  	/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
    
  1. The bundle.js file acts as an entry point for parcel-bundler to start from. Add the following code to bundle.js:
  2. // 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.

  1. Add the following to .sassrc:
  2. {
      "includePaths": [
        "./node_modules/"
      ]
    }
    

    This will allow referencing package folders without a full path to it. See parcel-bundler/parcel#39 for more information.

  1. Add the following code to site.scss:
  2.  	@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.

  1. Since we have no global scripts, we leave the site.js file empty for now.

  2. Add a scripts block to the package.json file right before the devDependencies: { line:

  3. "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.

  1. Now we build our bundle:
  2. 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.

  1. We need to update all references to the old files with the new ones instead. Remove all script tags in _Layout.cshtml and _ValidationScriptsPartial and add the following instead to _Layout.cshtml:
  2.  	<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.

Watching Changes

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 a Pre-Publish Task

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.

A Note on CommonJS Modules

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.

A few pointers and ideas

  • You do not have to use SCSS. LESS or even plain CSS is completely fine.
  • Parcel.js doesn’t have a config file of its own, unlike Grunt or webpack. You may, however, have config files for each tool, and parcel-bundler will honor them. E.g. tsconfig.json for typescript, .sassrc for SCSS, etc.
  • Parce.js has built-in support for PostCSS. For example, to automatically add CSS prefixes to the generated output using the autoprefixer-postcss plugin, add the following to .postcssrc at the project root:
    { 
      "plugins": { 
        "autoprefixer": true 
      } 
    }
    
  • You can also configure the browsers you wish to support by adding a .browserslistrc file.
  • You can create multiple bundles if you want. In our example, we put all the scripts in one file, but you could configure to put the validation scripts in a separate file instead.

Gildan Raphael
About the Author

Galdin Raphael

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

Related Posts

Comments

Comments are disabled in preview mode.