Created
September 23, 2015 11:58
-
-
Save SebastianStehle/258998d761dad255228c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// <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"]); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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>"; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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