We came up with a structure where our UI is a separate project and completely written in Elm while the backend is in ASP.NET.
The whole thing is in one Visual Studio solution and can be compiled using VS2015 or MSBuild in TeamCity.
We use VS Code for developing the UI though. I do not know of any Visual Studio support for Elm.
This is the project file (YourUI.csproj
) that is included in the UI project:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{B9D43DC2-6337-4605-BBE2-7C7C765EDC20}</ProjectGuid>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<BuildTask>build:dev</BuildTask>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<BuildTask>build:release</BuildTask>
</PropertyGroup>
<Target Name="Build">
<Exec Command="echo Executing npm install..." Condition="!Exists('$(ProjectDir)node_modules')" />
<Exec Command="npm install" Condition="!Exists('$(ProjectDir)node_modules')" />
<Exec Command="echo Building the UI using GULP..." />
<Exec Command="echo ---------------------------------------------" />
<Exec Command="gulp $(BuildTask)" />
<Exec Command="echo ---------------------------------------------" />
</Target>
<Target Name="Clean">
<Exec Command="gulp clean:all" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
<ItemGroup>
<None Include="readme.md" />
</ItemGroup>
</Project>
As you can see, in the background we use npm
and gulp
to do the actual work.
The important pieces from our gulp file (including the Elm CSS generation and WebPack bundling):
gulp.task('elm-init', function (cb) {
return gulp.src('src/*.elm')
.pipe(elm())
.pipe(gulp.dest('JS/generated'));
});
gulp.task('elm:dev', ['elm-init'], function (cb) {
return gulp.src('src/main.elm')
.pipe(elm.bundle('elm.js'), {
debug: true
})
.pipe(gulp.dest('JS/generated'));
});
gulp.task('elm:release', ['elm-init'], function (cb) {
return gulp.src('src/main.elm')
.pipe(elm.bundle('elm.js'))
.pipe(gulp.dest('JS/generated'));
});
bundle = function () {
return gulp.src('JS/index.js')
.pipe(webpackGulp({
output: {
filename: 'Ui.js',
}
}))
.pipe(gulp.dest('build/'));
}
gulp.task('bundle:dev', [/*project spcific things, */ 'elm:dev'], bundle);
gulp.task('bundle:release', [/*project spcific things, */ 'elm:release'], bundle);
gulp.task('css', function (cb) {
mkdirp('build/css', function (err) {
if (err) console.error(err)
});
exec(path.resolve('./node_modules/.bin/elm-css')
+ ' --pathToMake ./node_modules/.bin/elm-make'
+ ' src/Stylesheets.elm',
function (err, stdout, stderr) {
console.log(stdout);
console.log(stderr);
cb(err);
});
});
gulp.task('build:dev', function (cb) {
runSequence('clean:dev', 'bundle:dev', 'css');
});
The ASP.NET project file contains the following BeforeBuild
instructions:
<Target Name="BeforeBuild">
<MSBuild
Projects="..\YourUI\YourUI.csproj"
Condition="'$(Configuration)'=='Release' OR !(Exists('$(SolutionDir)YourUI\build\Ui.js') AND Exists('$(SolutionDir)YourUI\build\css'))"
Targets="Build" />
<Exec Command="echo Creating UI resources for $(Configuration)..." />
<!-- Delete old links -->
<Delete Files="$(ProjectDir)Scripts\Ui.js" />
<Delete Files="$(ProjectDir)Scripts\Ui.min.js" />
<RemoveDir Directories="$(ProjectDir)Content\Ui" />
<!---->
<!---->
<!-- DEBUG build-->
<!---->
<Exec Condition=" '$(Configuration)'=='Debug' " Command="mklink $(ProjectDir)Scripts\Ui.js $(SolutionDir)YourUI\build\Ui.js" />
<Exec Condition=" '$(Configuration)'=='Debug' " Command="mklink /d $(ProjectDir)Content\Ui $(SolutionDir)YourUI\build\css" />
<!---->
<!---->
<!-- RELEASE build-->
<!---->
<ItemGroup Condition=" '$(Configuration)'!='Debug' ">
<ElmApp Include="$(SolutionDir)YourUI\build\Ui.min.js" />
<ElmStyles Include="$(SolutionDir)YourUI\build\css\*.css" />
</ItemGroup>
<Copy Condition=" '$(Configuration)'!='Debug' " SourceFiles="@(ElmApp)" DestinationFolder="$(ProjectDir)Scripts" />
<Copy Condition=" '$(Configuration)'!='Debug' " SourceFiles="@(ElmStyles)" DestinationFolder="$(ProjectDir)Content\Ui" />
</Target>
<!-- Include the new files as content -->
<ItemGroup Condition=" '$(Configuration)'=='Debug' ">
<Content Include="Scripts\Ui.js" />
<Content Include="Content\Ui\*.css" />
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)'!='Debug' ">
<Content Include="Scripts\Ui.min.js" />
<Content Include="Content\Ui\*.css" />
</ItemGroup>