Categories
ASP.NET Core MSBuild

Compile SCSS At Build Time Visual Studio

Firstly, there are extensions that can compile scss at build time… but every extension installed is another barrier to new team members coming onboard and getting up to speed on a project. I believe the development environment should have as little dependencies as possible (including extensions). That said this approach does rely on Node.JS to compile the SCSS but since Node.JS has wide spread adoption, the chances are current (or potential) team members will have it already.

See the compiling SCSS at build time with visual studio GitHub repo for the full code base.

Initial Setup

To get started, lets create the default MVC application.

Initialise the default MVC template

Once it is has initialised, create a new folder in the root of the project called Scss. This is where we will store our raw .scss files. For simplicity of this example, we are going to have one file (site.scss). So we should have something that looks like this.

src folder structure

Add the following content to the site.scss file.

$green: #038503;

html h1 {
    color: $green;
}

NPM Setup

Open command prompt (note: PATH environment variable must contain npm directory for this to work).

Change directory into the folder where the project is located.

Run npm init

npm init creates an empty package.json for the project, which will later tell other machines the packages needed for this project to build.

Run npm i node-sass --save-dev

npm i node-sass --save-dev creates a dev dependency on the node-sass package which is what will be compiling the SCSS into CSS.

Add build-css script to package.json.

{
  "name": "compile-scss-on-build",
  "version": "1.0.0",
  "description": "This package does not do anything useful",
  "main": "index.js",
  "private": true,
  "dependencies": {},
  "devDependencies": {
    "node-sass": "^4.14.1"
  },
  "scripts": {
    "build-css": "node-sass --include-path src/scss --output-style compressed src/scss/site.scss --output wwwroot/css"
  },
  "author": "Jacob Dixon",
  "license": "MIT"
}

The scripts section defines commands the can be executed by calling npm run script-name. The build-css script executes node-sass while telling it to look in the src/scss folder for any missing files (--include-path src/scss), that we want the output to be compressed (--output-style compressed), the file to process (src/scss/site.scss this could be changed to the whole directory or specific files using glob patterns) and finally the output location (--output wwwroot/css).

Adding SCSS Compilation To Build

Now we have the npm script configured to build the src/scss folder we need to tell visual studio (or MSBuild more specifically) to run the command before building the project. Add the following lines to the projects .csproj file

<Target Name="PreBuild" BeforeTargets="PreBuildEvent">
    <Exec Command="npm run build-css" />
</Target>

The Target element allows us to extend the build with additional tasks (in the code above the task is Exec which we will get to shortly). The BeforeTargets attribute tells MSBuild to run these tasks before running the PreBuildEvent. Exec tells MSBuild to execute the specified command, in this case we are telling MSBuild to execute our npm script build-css.

The Microsoft Docs contains more information about the Target Element and Exec Task Element.

Adding Fast Build Support

If we run this now before adding fast build support we will see that it doesn’t actually build the scss when only the scss has changed. That is because FastBuild will do prechecks to look for changes and if it doesn’t find any, it will skip the build process. Normally this works great, but since we’ve added custom build steps to compile the scss, FastBuild isn’t aware of this and doesn’t know to check the src/scss folder.

Lets fix that now… add the following lines to the .csproj file.

<ItemGroup>
    <UpToDateCheckInput Include="src/scss/**/*.scss" Set="Css" />
    <UpToDateCheckBuilt Include="wwwroot/css/*.css" Set="Css" />
</ItemGroup>

The <UpToDateCheckInput Include="src/scss/**/*.scss" Set="Css" /> tells FastBuild to include any .scss files within the src/scss folder (including subfolders). The Set="Css" acts a group enabling the UpToDateCheckInput and UpToDateCheckBuilt commands to function in isolation to the rest of the FastBuild checks.

The <UpToDateCheckBuilt Include="wwwroot/css/*.css" Set="Css" /> line tells FastBuild to check the modified time of any .css files within the wwwroot/css folder. FastBuild will group the Input and Built checks because they are both within the css set and compare the latest modified time of .scss files against the earliest modified time of .css files. This means it’s important to make sure that only directories and files that will be directly modified as part of the build be included in the checks.

Debugging FastBuild

FastBuild can be tricky to understand what is going on, luckily we can enable logging within Visual Studio 2019.

To enable logging open Options (under Tools) inside Visual Studio 2019

Visual Studio 2019 Tools > Options

Navigate to Projects and Solutions > SDK-Style Projects

FastBuild options

I recommend setting it to Verbose for debugging issues. Verbose logging will output lines like this.

1>FastUpToDate: Comparing timestamps of inputs and outputs in set 'Css': (CompileScssOnBuild)
1>FastUpToDate: Adding UpToDateCheckBuilt outputs in set 'Css': (CompileScssOnBuild)
1>FastUpToDate:     'C:\VS Projects\compile-scss-at-build-time-with-visual-studio\CompileScssOnBuild\wwwroot\css\site.css' (CompileScssOnBuild)
1>FastUpToDate: Adding UpToDateCheckInput inputs in set 'Css': (CompileScssOnBuild)
1>FastUpToDate:     'C:\VS Projects\compile-scss-at-build-time-with-visual-studio\CompileScssOnBuild\src\scss\site.scss' (CompileScssOnBuild)
1>FastUpToDate: Input 'C:\VS Projects\compile-scss-at-build-time-with-visual-studio\CompileScssOnBuild\src\scss\site.scss' is newer (17/10/2020 22:55:58) than earliest output 'C:\VS Projects\compile-scss-at-build-time-with-visual-studio\CompileScssOnBuild\wwwroot\css\site.css' (17/10/2020 21:56:40), not up to date. (CompileScssOnBuild)
1>FastUpToDate: Up to date check completed in 18.5 ms (CompileScssOnBuild)

Summary

MSBuild and FastBuild are incredibly powerful and highly customisable tools that can be used to automate build tasks. Hopefully this example gives you the starting blocks to develop customised builds for your next project.

Leave a Reply

Your email address will not be published. Required fields are marked *