TypeScript bundling and minification? - asp.net-mvc

Assume I have two files
AFile.ts
/// <reference path="ZFile.ts" />
new Z().Foo();
ZFile.ts
class Z
{
Foo() { }
}
Is there a way to generate all scripts in a single js file in the order it requires (need ZFile before AFile to get the definition of Z)?

In post build events I added a call to TypeScript compiler
tsc "..\Content\Scripts\Start.ts" --out "..\Content\Scripts\all.js"
In the bundle configuration I added
bundles.Add(new ScriptBundle("~/scripts/all").Include("~/Content/Scripts/all.js"));
On the _Layout.cshtml file I added
#Scripts.Render("~/Scripts/all")
And with that I got
<script src="/Scripts/all?v=vsTcwLvB3b7F7Kv9GO8..."></script>
Which is all my script in a single file.
The compiler does not minify, you have to use bundles and compile on Release or set
BundleTable.EnableOptimizations = true;
You can also minify using Web Essentials or grabbing the contents and minifing somewhere else.

Now VS Typescript Extension supports merging to one file.
Make sure that you have installed the extension Tools -> Extensions and Updates (VS2015 has it by default)
Go to the project properties and check Combine JavaScript output into file:
Important to have /// <reference /> (as in question), it helps tsc order files by dependencies before the merge.
Then for minimisation bundle can be used as usual:
bundles.Add(new ScriptBundle("~/bundles/finale").Include("~/js/all.js"));
and in view
#Scripts.Render("~/bundles/finale")

Use the --out parameter.
tsc AFile.ts ZFile.ts --out single.js
The typescript compiler will do the dependency navigation for you automatically.

Assuming all of your ts files are directly or indirectly under a folder called say 'ts' you could write a tt script which merged all of .js files(but not min.js) into a file myApp.js and all of your min.js files into myApp.min.js.
To obtain the ordering of files you could process subfolders thus:
string[] FolderOrder =
{
#"libs\utils\",
#"libs\controls\",
#"app\models",
#"app\viewmodels",
#".",
};

Related

Using NPM/Bower over Nuget MVC Site with bundling

I am trying to train myself on the new preferred way of referencing Front-end libraries in .Net Applications, so I have started to read into doing this with Gulp. I have read this article on replacing the Nuget packages with ones from bower
https://blogs.msdn.microsoft.com/cdndevs/2015/02/17/using-bower-with-visual-studio/
And this one to use Gulp
http://www.davepaquette.com/archive/2014/10/08/how-to-use-gulp-in-visual-studio.aspx
However I am having some trouble putting it all together. I want together some process that will not only replace all the Pre-Installed Nuget packages with bower packages, but also minify and bundle them with gulp instead of using the Web.Optimization library. The first link walks through doing that, but the gulp script is not providing the output I would expect (no dist folders for instance). This SO question has some good answers, but I am not seeing how I bundle all the libraries from bower (I read through all the comments and answers).
Using Grunt, Bower, Gulp, NPM with Visual Studio 2015 for a ASP.NET 4.5 Project
Obviously this stuff is new to me so I will get confused, but I want to make sure I do it the right way.
I am using Visual Studio 2015 and am creating a new MVC 4.5.2 Application, and I want to have all front-end libraries referenced and bundled/minified without using any .Net libraries. It seems at this point far easier to do it the old way
The question is a little broad but Ill hit on the key points since I've been doing this exact thing for a few weeks now.
Break what you are doing into two phases - replacing packages from nuget as phase one and replacing the .net bundling as the other.
Phase one - pretty simple actually, remove (uninstall) all the packages you have from nuget that have bower equivalents, then add those packages via the bower commands (don't forget to --save and --save-dev where needed). Then replace your script locations in the .net bundling with the new locations in bower_components. Once you can confirm your site works under this new layout while still using .net bundling you are ready for phase two.
Now for phase two, first and formost you need to learn the concept of 'globs' which are basically wild card based include and excludes for files in gulp. Probably the single most helpful thing I've found to help with this is the gulp plugin main-bower-files. So to create a good glob to start with I had this...
var paths = {};
paths.baseReleaseFolder = "app";
paths.baseJSReleaseFile = "site.min.js";
paths.jsSource = mainBowerFiles();//this gets all your bower scripts in an array
.concat([
"node_modules/angular/angular.js",//my angular is loaded via npm not bower because the bower version was giving me dependency issues (bug)
"node_modules/angular-route/angular-route.js",
"Source/output.js",//generated by MY typescript compiler
"!/Scripts", //this is an exclude
"!**/*.min.js" //also an exclude
]);
This is basically saying I want to select all the DISTRO bower plugins files needed to run whatever, include my angular js, and exclude anything in my scripts folder (typescript files and outputs) and exclude any ALREADY minified files (I want to do it all myself as one concatenated file).
Now I separate the js and css operations and wrap another event to call both so I end up with
gulp.task("min", ["min:js", "min:css"]);
gulp.task("min:js", function () {});
gulp.task("min:css", function () {});
Now as an example of how that works I have the following in my min:js
gulp.task("min:js", function () {
var jsFilter = filter('**/*.js', { restore: true });//extra file safty incase my glob is getting non js files somehow
return gulp
.src(paths.jsSource) //this is the 'glob' IE the file selector
.pipe(jsFilter) //enforce my filter from above (gulp-filter)
//.pipe(debug()) //useful to output what files are in use (gulp-debug)
.pipe(sourcemaps.init({loadMaps:true})) //create sourcemaps for final output(gulp-sourcemaps)
.pipe(uglify()) //min and ugilfy files on the way to next step (gulp-uglify)
.pipe(concat(paths.baseReleaseFolder + "/" + paths.baseJSReleaseFile)) //this takes all the files in the glob and creates a single file out of them in app/site.min.js
.pipe(rev()) //ignore this, cache-busting but requires work on the .net side to load the files, basically adds the file hash to the file name in the output
.pipe(sourcemaps.write(".")) //actually write my .map.js file to the final destination
.pipe(gulp.dest(".")) //write the final site.min.js to its location
.pipe(jsFilter.restore); //not sure but all filter examples do this.
});
So when this is all said and done I end up with a single site.min.js file in the 'app' folder that is the concatenated, minified, uglified version off all my bower components (and whatever else the glob hit). Now just to give you an idea on how plugin intensive using gulp is this is the declaration of all plugins I load into my main gulp script to do bascailly what .net bundling does for you....
var gulp = require('gulp'),
rimraf = require("gulp-rimraf"),
concat = require("gulp-concat"),
cssmin = require("gulp-cssmin"),
debug = require("gulp-debug"),
uglify = require("gulp-uglify"),
filter = require("gulp-filter"),
rename = require("gulp-rename"),
rev = require('gulp-rev'),
sourcemaps = require('gulp-sourcemaps'),
csslint = require('gulp-csslint'),
jslint = require('gulp-jslint'),
typescript = require("gulp-typescript"),
tslint = require("gulp-tslint"),
runSequence = require('run-sequence'),
mainNodeFiles = require("npm-main-files"),
mainBowerFiles = require('main-bower-files');
You probably don't need all of these (some are typescript, some are linters)
Edit: Heres my css function
gulp.task("min:css", function () {
var cssFilter = filter('**/*.css', { restore: true });
return gulp
.src(paths.cssSource)
.pipe(cssFilter)
//.pipe(debug())
.pipe(sourcemaps.init({ loadMaps: true }))
.pipe(concat(paths.baseReleaseFolder + "/" + paths.baseCSReleaseFile))
.pipe(cssmin())
.pipe(rev())
.pipe(sourcemaps.write("."))
.pipe(gulp.dest("."))
.pipe(cssFilter.restore);
});

How to efficiently manage and process Bower packages with Gulp? (VS2015, Visual Studio 2015)

Background:
Visual Studio 2015 has introduced Gulp and Bower for client side package management. .Net previously had a very efficient method of bundling / minification and package management, but for an unspecified reason this has been removed in ASP.Net 5 / MVC 6, and the advice is to use Gulp and Bower instead.
I have a number of vendor files that I wish to use in my project, including jquery.appear, isotope, owl-carousel etc, etc; some are simple JavaScript files, others have CSS, still others have assets such as fonts, images.
Scenario:
At the moment I am evaluating how to best utilise Bower to manage versions of packages, while using Gulp to extract only the necessary files from bower_components, and uglify / minify / concat them into bundles.
I am currently using CDN available versions of scripts, but best practice would suggest I implement fail-over to local copies - IF I can find a way to manage them using Bower / Gulp OR just download them locally, and forgo package management.
Package management would be my preferred approach, but not if this is high maintenance in terms of scripts, configurations, overrides etc.
What I have tried:
I have looked at Gulp packages such as bower-main-files, gulp-bower-src (which apparently is blacklisted by Gulp), and I am currently using gulp-concat-vendor; with this I can process basic packages which contain only single JavaScript files (i.e. not CSS, not related assets such as images).
Problems:
Some of the bower packages do not include correct information for exporting their main files (some have no main declarations at all).
Some of the packages download dependencies into bower_components at the top level, which becomes cluttered with files I do not need (I want only the main (core) exported files, and the dependencies are usually already met elsewhere). These additional packages need yet more configuration to exclude them from being processed as part of 'Bower Main Files'.
In general, Bower 'standards' are loose, and are not adhered to, even for popular packages.
During concatenation, sometimes a specific order needs to be achieved. I have been unable to find an elegant way to do this automatically - I have created an array of source files, but this is not ideal, as it requires manually checking and editing for each package, which mostly negates the whole concept of package management.
Questions:
Do experienced front-end developers attempt to follow the same approach as I am attempting (using bower_components as a source), or simply manually copy required files from GitHub?
If you do use bower-components, can you please outline the workflow with Gulp, and what plug-ins you use to filter out only the files you need.
Is it possible to prevent unneeded dependencies, tests, etc from being downloaded by Bower in the first place?
When processing files that include relative references (e.g. CSS containing a reference to an image), is it possible to correct the relative path, to be relative to the specified output directory for such assets?
Yes.
See below.
Well, bower package is package, you get whats included. For your build you either rely on components bower.json which specifies main files or do filtering yourself. It is simple enough.
You can use filter = require('gulp-filter') to filter files like that:
var gulp = require('gulp'),
bower = require('gulp-main-bower-files'),
filter = require('gulp-filter'),
concat = require('gulp-concat'),
rename = require('gulp-rename'),
srcmaps = require('gulp-sourcemaps'),
jsminify = require('gulp-uglify')
cssminify = require('gulp-csso'),
del = require('del');
var src = {
js: 'app/**/*.js',
css: 'app/**/*.css',
content: ['app/**/*.jpg', 'app/**/*.svg', 'app/**/*.png', 'app/**/*.ico', 'app/**/*.html']
}
var dst = {
pub: 'pub/',
lib: 'pub/lib/'
}
gulp.task('bower', ['start-build'], function () {
var jsfilter = filter('**/*.js')
var cssfilter = filter('**/*.css')
return gulp.src('bower.json')
.pipe(bower())
.pipe(jsfilter)
.pipe(concat('lib.min.js'))
.pipe(jsminify())
.pipe(gulp.dest(dst.lib))
.pipe(jsfilter.restore())
.pipe(cssfilter)
.pipe(concat('lib.min.css'))
.pipe(cssminify())
.pipe(gulp.dest(dst.lib))
.pipe(cssfilter.restore())
.pipe(rename(function (path) {
if (~path.dirname.indexOf('fonts')) {
path.dirname = '/fonts'
}
}))
.pipe(gulp.dest(dst.lib));
})
gulp.task('js', ['start-build'], function () {
return gulp.src([src.js])
.pipe(srcmaps.init())
.pipe(concat('app.min.js'))
.pipe(jsminify())
.pipe(srcmaps.write())
.pipe(gulp.dest(dst.pub));
})

How to implement a Groovy global AST transformation in a Grails plugin?

I'd like to modify some of my Grails domain classes at compilation time. I initially thought this was a job for Groovy's global ASTTransformation since I don't want to annotate my domain classes (which local transformers require). What's the best way to do this?
I also tried mimicking DefaultGrailsDomainClassInjector.java by creating my own class in the same package, implementing the same interfaces, but I probably just didn't know how to package it up in the right place because I never saw my methods get invoked.
On the other hand I was able to manually create a JAR which contained a compiled AST transformation class, along with the META-INF/services artifacts that plain Groovy global transformations require. I threw that JAR into my project's "lib" dir and visit() was successfully invoked. Obviously this was a sloppy job because I am hoping to have the source code of my AST transformation in a Grails plugin and not require a separate JAR artifact if I don't have to, plus I couldn't get this approach to work by having the JAR in my Grails plugin's "lib" but had to put it into the Grails app's "lib" instead.
This post helped a bit too: Grails 2.1.1 - How to develop a plugin with an AstTransformer?
The thing about global transforms the transform code should be available when the compilation starts. Having the transformer in a jar was what i did first! But as you said it is a sloppy job.
What you want to do is have your ast transforming class compile before others gets to the compilation phase. Here is what you do!
Preparing the transformer
Create a directory called precompiled in src folder! and add the Transformation class and the classes (such as annotations) the transformer uses in this directory with the correct packaging structure.
Then create a file called org.codehaus.groovy.transform.ASTTransformation in called precompiled/META-INF/services and you will have the following structure.
precompiled
--amanu
----LoggingASTTransformation.groovy
--META-INF
----services
------org.codehaus.groovy.transform.ASTTransformation
Then write the fully qualified name of the transformer in the org.codehaus.groovy.transform.ASTTransformation file, for the example above the fully qualified name would be amanu.LoggingASTTransformation
Implementation
package amanu
import org.codehaus.groovy.transform.GroovyASTTransformation
import org.codehaus.groovy.transform.ASTTransformation
import org.codehaus.groovy.control.CompilePhase
import org.codehaus.groovy.ast.ASTNode
import org.codehaus.groovy.control.SourceUnit
#GroovyASTTransformation(phase=CompilePhase.CANONICALIZATION)
class TeamDomainASTTransformation implements ASTTransformation{
public void visit(ASTNode[] nodes, SourceUnit sourceUnit) {
println ("*********************** VISIT ************")
source.getAST()?.getClasses()?.each { classNode ->
//Class node is a class that is contained in the file being compiled
classNode.addProperty("filed", ClassNode.ACC_PUBLIC, new ClassNode(Class.forName("java.lang.String")), null, null, null)
}
}
}
Compilation
After implementing this you can go off in two ways! The first approach is to put it in a jar, like you did! and the other is to use a groovy script to compile it before others. To do this in grails, we use _Events.groovy script.
You can do this from a plugin or the main project, it doesn't matter. If it doesn't exist create a file called _Events.groovy and add the following content.
The code is copied from reinhard-seiler.blogspot.com with modifications
eventCompileStart = {target ->
...
compileAST(pluginBasedir, classesDirPath)
...
}
def compileAST(def srcBaseDir, def destDir) {
ant.sequential {
echo "Precompiling AST Transformations ..."
echo "src ${srcBaseDir} ${destDir}"
path id: "grails.compile.classpath", compileClasspath
def classpathId = "grails.compile.classpath"
mkdir dir: destDir
groovyc(destdir: destDir,
srcDir: "$srcBaseDir/src/precompiled",
classpathref: classpathId,
stacktrace: "yes",
encoding: "UTF-8")
copy(toDir:"$destDir/META-INF"){
fileset(dir:"$srcBaseDir/src/precompiled/META-INF")
}
echo "done precompiling AST Transformations"
}
}
the previous script will compile the transformer before others are compiled! This enable the transformer to be available for transforming your domain classes.
Don't forget
If you use any class other than those added in your classpath, you will have to precompile those too. The above script will compile everything in the precompiled directory and you can also add classes that don't need ast, but are needed for it in that directory!
If you want to use domain classes in transformation, You might want to do the precompilation in evenCompileEnd block! But this will make things slower!
Update
#Douglas Mendes mentioned there is a simple way to cause pre compilation. Which is more concise.
eventCompileStart = {
target -> projectCompiler.srcDirectories.add(0, "./src/precompiled")
}

Custom script transform in ASP.NET MVC bundle ignored when debug is false and minified file exists

I'm working on an MVC 5.0 (.Net 4.5) application where I need to apply a custom JavaScript transform to my included bundle files. One of those files, which I'm calling dummy.js for illustration purposes, has a minified file called dummy.min.js.
I created a custom script transform to replace injected window.jQuery references with a different expression. Everything works fine when I run locally and in debug mode, but when debug mode is turned off in the Web.config file, the Bundle returns the contents of the dummy.min.js file, but my script transform is not applied to it. It only gets applied to JavaScript files that don't have an associated .min.js file.
Does anyone have an idea on how to resolve this? It almost sounds like a bug in MVC.
A workaround is to remove the minified file. This post kind of addresses my situation by suggesting removing the .min.js file since MVC minifies by default, but I'm looking for an alternative solution (if any).
Thank you so much in advance.
Here's how to reproduce the above:
If you're interested in reproducing my issue, here's a quick BundleConfig and the actual custom script transform. It replaces all instances of window.jQuery with window.$jq1_9||window.jQuery, assuming it is injected via a self-executing anonymous function.
public class BundleConfig
{
public static void RegisterBundles(BundleCollection bundles)
{
bundles.Add(
new ScriptBundle("~/bundles/dummy")
.Include("~/Scripts/dummy.js", new InjectedJQueryVariableRewriteTransform()));
}
}
public class InjectedJQueryVariableRewriteTransform : System.Web.Optimization.IItemTransform
{
public string Process(string includedVirtualPath, string javaScriptCode)
{
// TODO: I understand this approach is naiive, but it does the trick for now.
return javaScriptCode.Replace("window.jQuery", "window.$jq1_9 || window.jQuery");
}
}
If you have Visual Studio 2012 and MVC 4, you will need version 1.1.0 of the System.Web.Optimization assembly, which you can obtain by running the following command in the Nuget Package Manager. At time of writing it installs version 1.1.2 of the package.
Install-Package Microsoft.AspNet.Web.Optimization
Here's the sample JavaScript dummy.js. You can create a copy of it and name it dummy.min.js:
(function ($) {
"use strict";
// TODO: Do something interesting...
})(window.jQuery);
Set the debug attribute to false in the following element in Web.config:
<compilation debug="false" targetFramework="4.5" />
Assuming the application's port is 9221, render the bundle in Firefox or Chrome:
http://localhost:9221/bundles/dummy
You will see that when debug is set to true, the transform is applied, as shown below:
(function(){"use strict"})(window.$jq1_9||window.jQuery)
When it is set to false. It is ignored and only the .min.js file is used:
(function(){"use strict"})(window.jQuery)
If you add this line:
bundles.FileExtensionReplacementList.Clear();
you will remove the rule for using .min files when bundling is enabled. You will remove all rules, unfortunately, so if you need any of the other ones you'll need to add them manually. Also, this will change the rules for all bundles.
If you just want to disable these replacement rules for just one bundle, you can just set the EnableFileExtensionReplacements property to false on that specific bundle:
var bundle = new ScriptBundle("...");
bundle.EnableFileExtensionReplacements = false;

How to make web_ui compile css files automatically

I'm using web_ui and whenever I change a CSS file in web/css/ it will not be compiled unless I change the web/index.html file. I guess that's because only the file 'web/index.html' is listed as entry point in build.dart.
But adding the stylesheet to the entry points list didn't work.
Is there a way to autocompile CSS files every time they are changed without having to edit the .html files?
Keep in mind that you can edit any .dart or .html file and the compiler will run; it doesn't have to be the entry point file.
Autocompilation of CSS files on change can be achieved by passing the compiler the full flag:
build(['--machine', '--full'], ['web/index.html']);
The machine flag tells the compiler to print messages to the Dart Editor console. For a full list of flags see Build.dart and the Dart Editor Build System.
This method means that every time a file is changed your entire project will be rebuilt instead of the usual incremental approach. If you have a large project this may take a while. Here is a more comprehensive build file that takes advantage of incremental compilation and only rebuilds the whole project if a css file was changed:
List<String> args = new Options().arguments;
bool fullRebuild = false;
for (String arg in args) {
if (arg.startsWith('--changed=') && arg.endsWith('.css')) {
fullRebuild = true;
}
}
if(fullRebuild) {
build(['--machine', '--full'], ['web/index.html']);
} else {
build(args, ['web/index.html']);
}

Resources