Skip to content

Instantly share code, notes, and snippets.

@SebastianStehle
Created September 23, 2015 11:58
Show Gist options
  • Save SebastianStehle/258998d761dad255228c to your computer and use it in GitHub Desktop.
Save SebastianStehle/258998d761dad255228c to your computer and use it in GitHub Desktop.
using Microsoft.AspNet.FileProviders;
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Runtime;
using System;
using System.IO;
using System.Text;
namespace BundleHelpers
{
public abstract class BundleTagHelper : TagHelper
{
public const string AttributeBase = "base";
public const string AttributeFile = "file";
public const string AttributeBundle = "bundle";
public const string AttributeMinified = "minified";
private readonly IApplicationEnvironment applicationEnvironment;
private readonly IHostingEnvironment hostingEnvironment;
[HtmlAttributeName(AttributeBase)]
public string Base { get; set; }
[HtmlAttributeName(AttributeFile)]
public string File { get; set; }
[HtmlAttributeName(AttributeBundle)]
public bool UseBundle { get; set; }
[HtmlAttributeName(AttributeMinified)]
public bool UseMinified { get; set; }
[HtmlAttributeNotBound]
[ViewContext]
protected ViewContext Context { get; set; }
protected abstract bool AppendVersion { get; }
protected abstract string MinExtension { get; }
protected abstract string Extension { get; }
protected BundleTagHelper(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment)
{
this.applicationEnvironment = applicationEnvironment;
this.hostingEnvironment = hostingEnvironment;
}
protected abstract string BuildTag(string file);
public override void Process(TagHelperContext context, TagHelperOutput output)
{
string file = File;
if (!string.IsNullOrWhiteSpace(file))
{
file = HtmlFileHelper.ReplaceCultureTag(file);
if (file.EndsWith(".bundle", StringComparison.OrdinalIgnoreCase))
{
if (!UseBundle)
{
AppendBundleFiles(output, file);
}
else
{
AppendBundledFile(output, file);
}
}
else
{
AppendSimpleFile(output, file);
}
output.TagName = string.Empty;
}
base.Process(context, output);
}
private void AppendBundleFiles(TagHelperOutput output, string file)
{
StringBuilder mvcHtmlStringBuilder = new StringBuilder();
mvcHtmlStringBuilder.Append("<!-- BUNDLE START: ");
mvcHtmlStringBuilder.Append(file);
mvcHtmlStringBuilder.AppendLine("-->");
int slashIndex = file.LastIndexOf('/');
string directory = MakeAbsolutePath((slashIndex >= 0 ? file.Substring(0, slashIndex) : file).Trim('/'));
IFileInfo bundleFile = hostingEnvironment.WebRootFileProvider.GetFileInfo(NormalizePath(file));
if (bundleFile.Exists)
{
using (Stream stream = bundleFile.CreateReadStream())
{
using (StreamReader reader = new StreamReader(stream))
{
string includedPath;
while ((includedPath = reader.ReadLine()) != null)
{
if (includedPath.StartsWith("//= include", StringComparison.OrdinalIgnoreCase))
{
includedPath = includedPath.Replace("//= include ", string.Empty);
includedPath = includedPath.TrimStart('/');
includedPath = directory + "/" + includedPath;
includedPath = AdjustContentPath(includedPath);
string tag = BuildTag(includedPath);
mvcHtmlStringBuilder.AppendLine(tag);
}
}
}
}
}
mvcHtmlStringBuilder.Append("<!-- BUNDLE END: ");
mvcHtmlStringBuilder.Append(file);
mvcHtmlStringBuilder.AppendLine("-->");
output.PostContent.Append(mvcHtmlStringBuilder.ToString());
}
private void AppendBundledFile(TagHelperOutput output, string file)
{
file = file.Replace(".bundle", Extension);
file = AdjustContentPath(file);
string tag = BuildTag(file);
output.PostContent.Append(tag);
}
private void AppendSimpleFile(TagHelperOutput output, string file)
{
file = AdjustContentPath(file);
string tag = BuildTag(file);
output.PostContent.Append(tag);
}
protected internal virtual string NormalizePath(string path)
{
if (path.StartsWith("~/", StringComparison.Ordinal))
{
return path.Substring(1).Replace('\\', '/');
}
if (path.StartsWith("~\\", StringComparison.Ordinal))
{
return path;
}
return path.Replace('\\', '/');
}
private string AdjustContentPath(string path)
{
if (UseMinified && !string.IsNullOrWhiteSpace(MinExtension) && !path.EndsWith(MinExtension, StringComparison.OrdinalIgnoreCase))
{
path = path.Replace(Extension, MinExtension);
}
if (AppendVersion)
{
path += "?v=" + applicationEnvironment.Version;
}
path = MakeAbsolutePath(path);
return path;
}
private string MakeAbsolutePath(string path)
{
string basePath = Base;
if (string.IsNullOrWhiteSpace(basePath))
{
basePath = Context.HttpContext.Request.PathBase;
}
if (!basePath.EndsWith("/", StringComparison.OrdinalIgnoreCase))
{
basePath = basePath + "/";
}
path = basePath + path.TrimStart('/', '~').ToLowerInvariant();
return path;
}
}
}
/// <binding Clean='clean' />
var gulp = require("gulp"),
rimraf = require("gulp-rimraf"),
include = require("gulp-include"),
concat = require("gulp-concat"),
cssmin = require("gulp-minify-css"),
gutil = require('gulp-util'),
uglify = require("gulp-uglify"),
rename = require("gulp-rename"),
replace = require("gulp-replace"),
less = require("gulp-less"),
livereload = require('gulp-livereload'),
tsc = require('gulp-typescript'),
debug = require('gulp-debug'),
sequence = require('run-sequence');
project = require("./project.json");
tsConfig = require("./tsconfig.json");
var webroot = "./" + project.webroot + "/";
var js = {
srcTs: webroot + "scripts/ts/**/*.ts",
outFolderTs: webroot + "scripts/ts/",
outFilesTs: webroot + "scripts/ts/**/*.js",
typingsTs: webroot + "scripts/ts/typings/**/*.d.ts",
srcBundles: webroot + "scripts/*.bundle",
outFolderBundles: webroot + "scripts/",
outFilesBundles: webroot + "scripts/*.js",
srcMin: webroot + "scripts/*.js",
outFolderMin: webroot + "scripts/",
outFilesMin: webroot + "scripts/*.min.js"
};
var css = {
srcLess: webroot + "styles/less/**/*.less",
incLess: webroot + "styles/less/**/_*.less",
outFolderLess: webroot + "styles/less",
outFilesLess: webroot + "styles/less/**/*.css",
srcBundles: webroot + "styles/*.bundle",
outFolderBundles: webroot + "styles/",
outFilesBundles: webroot + "styles/*.css",
srcMin: webroot + "styles/*.css",
outFolderMin: webroot + "styles/",
outFilesMin: webroot + "styles/*.min.css"
};
gulp.task("ts:clean", function () {
return gulp.src([js.outFilesTs])
.pipe(debug({ title: "delete:" }))
.pipe(rimraf());
});
gulp.task("ts:build", function () {
return gulp.src([js.srcTs, "!" + js.typingsTs])
.pipe(debug({ title: "build:" }))
.pipe(tsc(tsConfig.compilerOptions))
.pipe(gulp.dest(js.outFolderTs));
});
gulp.task("js:clean", function () {
return gulp.src([js.outFilesBundles])
.pipe(debug({ title: "delete:" }))
.pipe(rimraf());
});
gulp.task("js:bundle", function () {
return gulp.src(js.srcBundles)
.pipe(debug({ title: "bundle:" }))
.pipe(include())
.pipe(rename({
extname: ".js"
}))
.pipe(gulp.dest(js.outFolderBundles));
});
gulp.task("js:min", function () {
return gulp.src([js.srcMin, "!" + js.outFilesMin])
.pipe(debug({ title: "min:" }))
.pipe(uglify())
.pipe(rename({
extname: ".min.js"
}))
.pipe(gulp.dest(js.outFolderMin));
});
gulp.task("scripts:clean", ["ts:clean", "js:clean"]);
gulp.task("scripts:rebuild", function (cb) {
sequence("scripts:clean", "ts:build", "js:bundle", "js:min", cb);
});
gulp.task("less:clean", function () {
return gulp.src([css.outFilesLess])
.pipe(debug({ title: "delete:" }))
.pipe(rimraf());
});
gulp.task("less:build", function () {
return gulp.src([css.srcLess, "!" + css.incLess])
.pipe(debug({ title: "build:" }))
.pipe(less())
.pipe(gulp.dest(css.outFolderLess))
.pipe(livereload());
});
gulp.task("css:clean", function () {
return gulp.src([css.outFilesBundles])
.pipe(debug({ title: "delete:" }))
.pipe(rimraf());
});
gulp.task("css:bundle", function () {
return gulp.src(css.srcBundles)
.pipe(debug({ title: "bundle:" }))
.pipe(include())
.pipe(rename({
extname: ".css"
}))
.pipe(gulp.dest(css.outFolderBundles));
});
gulp.task("css:min", function () {
gulp.src([css.srcMin, "!" + css.outFilesMin])
.pipe(debug({ title: "min:" }))
.pipe(cssmin())
.pipe(replace(/(\.\.\/){2,}/g, "../"))
.pipe(rename({
extname: ".min.css"
}))
.pipe(gulp.dest(css.outFolderMin));
});
gulp.task("styles:clean", ["less:clean", "css:clean"]);
gulp.task("styles:rebuild", function (cb) {
sequence("styles:clean", "less:build", "css:bundle", "css:min", cb);
});
gulp.task("dev:less", function () {
livereload.listen();
gulp.watch(css.srcLess, ["less:build"]);
});
gulp.task("dev:ts", function () {
gulp.watch(js.srcTs, ["ts:build"]);
});
gulp.task("dev", ["dev:less", "dev:ts"]);
using System.Globalization;
namespace BundleHelpers
{
public static class HtmlFileHelper
{
public static string ReplaceCultureTag(string input)
{
input = ReplaceLongCultureInPath(input);
input = ReplaceShortCultureInPath(input);
return input;
}
private static string ReplaceLongCultureInPath(string path)
{
string cultureTag = "{culture}";
if (path.Contains(cultureTag))
{
path = path.Replace(cultureTag, CultureInfo.CurrentCulture.Name);
}
return path;
}
private static string ReplaceShortCultureInPath(string path)
{
string cultureTag = "{shortculture}";
if (path.Contains(cultureTag))
{
path = path.Replace(cultureTag, CultureInfo.CurrentCulture.TwoLetterISOLanguageName.ToLowerInvariant());
}
return path;
}
}
}
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Runtime;
namespace BundleHelpers
{
[TargetElement("script-bundle", Attributes = AttributeFile)]
public class ScriptBundleHelper : BundleTagHelper
{
protected override bool AppendVersion
{
get
{
return true;
}
}
protected override string Extension
{
get
{
return ".js";
}
}
protected override string MinExtension
{
get
{
return ".min.js";
}
}
public ScriptBundleHelper(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment)
: base(applicationEnvironment, hostingEnvironment)
{
}
protected override string BuildTag(string file)
{
return $"<script type=\"text/javascript\" src=\"{file}\"></script>";
}
}
}
using Microsoft.AspNet.Hosting;
using Microsoft.AspNet.Razor.Runtime.TagHelpers;
using Microsoft.Framework.Runtime;
namespace BundleHelpers
{
[TargetElement("style-bundle", Attributes = AttributeFile)]
public class StyleBundleTagHelper : BundleTagHelper
{
protected override bool AppendVersion
{
get
{
return true;
}
}
protected override string Extension
{
get
{
return ".css";
}
}
protected override string MinExtension
{
get
{
return ".min.css";
}
}
public StyleBundleTagHelper(IApplicationEnvironment applicationEnvironment, IHostingEnvironment hostingEnvironment)
: base(applicationEnvironment, hostingEnvironment)
{
}
protected override string BuildTag(string file)
{
return $"<link type=\"text/css\" rel=\"stylesheet\" href=\"{file}\" />";
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment