Using NPM/Bower over Nuget MVC Site with bundling - asp.net-mvc

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);
});

Related

Generate seperate build file for a particular component in angular 7

I am going to develop a very large application using Angular 7 Framework.
I have created a blank angular workspace using
ng new angular-app --create-application=false
And in this workspace I have two angular applications created using the following commands:
ng generate application app-one
ng generate application app-two
Inside each of the two applications, I am going to have multiple components each working independently of each other.
I am looking for a way to create a separate javascript build file for each of the component so as to reduce the build size.
And use each of the separately build js files to use each component as a web component.
Please read what I have already tried to get a better idea.
I have tried the following steps:
Create a repository with prefix custom for custom angular elements:
ng new app-name --prefix custom
Add the angular elements package:
ng add #angular/elements
Create custom element component with encapsulation as native/emulated/none as required:
ng g component my-component --inline-style --inline-template -v Native
Define the custom element in app.modulte.ts
import { Injector} from '#angular/core';
import { createCustomElement } from '#angular/elements';
...
export class AppModule {
constructor(private injector : Injector){
const el = createCustomElement(MyComponent, {injector : this.injector});
customElements.define('my-component',el);
}
ngDoBootstrap(){ }
}
Install ngx-build-plus package for building a single bundle (e. g. for Angular Elements):
npm i ngx-build-plus
Update application's builder section within the angular.json file so that it points to ngx-build-plus:
"builder": "ngx-build-plus:build",
Add script in package.json to run builder:
"build:ngx": "ng build --prod --output-hashing none --single-bundle true"
If required, Combine scripts.js and main.js in the created dist folder by creating a js file "concat_ngx.js":
const fs = require('fs-extra');
const concat = require('concat');
(async function build() {
const files = [
'./dist/<your_project>/scripts.js',
'./dist/<your_project>/main.js',
]
await fs.ensureDir('elements_ngx')
await concat(files, 'elements_ngx/combined-script.js');
})()
Run file to get single js file:
node concat_ngx.js
Use js file in any Angular/Other project to use the custom component created.
But the problem here is I have to change the component bootstrap every time in app-module.ts
I needed an automated way to change the bootstrapping in app-module.ts at runtime.
But the problem here is I have to change the component bootstrap every time in app-module.ts
I needed an automated way to change the bootstrapping in app-module.ts at runtime.
In Angular 7, Add Default or Automatic bootstrapping :-
It is the default way an angular application bootstraps and main.ts holds the starting point of an application.
platformBrowserDynamic().bootstrapModule(AppModule);
For more information see here: -https://medium.com/learnwithrahul/ways-of-bootstrapping-angular-applications-d379f594f604
ng serve -o app-one
or
ng serve -o app-two
For more information see here https://medium.com/#klauskpm/change-the-default-angular-project-27da8fca8721

Using StealJS to load Bower components lacking a bower.json file

I am using the StealJS + Bower integration in my application, but a couple of my Bower components (including es6-collections) do not contain a bower.json file. Because es6-collections is one of the dependencies in my project's bower.json file, StealJS tries to load the es6-collections component's bower.json file, cannot find it because it does not exist, and complains: Unable to load the bower.json for es6-collections. I tried using System.config({ path: { 'es6-collections': '...' } }) to notify StealJS of the path to the script to use when loading es6-collections, but that does not help. What can I do to get StealJS to load this component?
Assumptions
So I am going to make a few assumptions:
you are using steal from bower
you are "bower install"'ing es6-collections from github directly
you are implicitly using the system-bower plugin by using the HTML <script src="bower_components/steal/steal.js" main="main"></script> to load your "main" file
If these things seem mostly true-ish then you may just have to add some configuration in your bower.json file to silence the error/warning and have everything work as expected.
Explanation:
So because the system-bower plugin (which you are using implicitly because steal detects it is being loaded from a bower_components directory) uses the components bower.json files to determine entry points, so in this case the error/warning comes from not being able to find es6-collections bower.json file.
Solution:
So we just need to tell System (used by steal) where to find that module and that it can stop looking for it's bower.json file.
We can do that by adding a "system" property to the bower.json and adding some configuration data like this...
"system": {
"paths": {
"es6-collections": "bower_components/es6-collections/index.js"
},
"bowerIgnore": ["es6-collections"],
"meta": {
"es6-collections": {
"format": "global"
}
}
}
The paths configuration there, tells System where to find the module
The bowerIgnore array tells system-bower to not look for the bower.json for that module
and the meta config is there to tell System to treat this module like it's a script that is going to add to the global object (window in the browser), which you should probably do for this particular module because of the way es6-collections was written: it exports an empty object if it has nothing to pollyfill so you can't use the exported object, best to just use it as if it was a global module.
For more information on all these things...
http://stealjs.com/docs/bower.html
https://github.com/systemjs/systemjs/wiki/Meta-Configuration
http://stealjs.com/docs/steal.html
Just to have a working example here https://gist.github.com/BigAB/c108bb0860c9cfee3d6a are three files you can copy-paste/clone and then do a bower install and see it working.

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));
})

TypeScript bundling and minification?

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",
#".",
};

How should I move or delete files in a Yeoman Generator?

I'm building a generator that in part includes scaffolding from another project created with exec. Depending on user input I need to move or delete parts of this scaffolding.
Right now I'm doing it with node's fs.child_process.spawn and shelljs, but seeing as the Yo generator has mkdir, write, template, and copy, I'm wondering if there's a Yo way to move or delete files and directories.
I just use rimraf like this:
MyGenerator.prototype.removeDir = function removeDir () {
var cb = this.async(),
self = this;
rimraf('path/to/dir', function () {
self.log.info('Removing dir');
cb();
});
};
Remember to add rimraf as a dependency in your package.json file. Not sure if there's a built-in function for this but this one's been working fine for me so far.
Yeoman now supports this via the fs API, which is an in memory filesystem implementation.
this.fs.move('source/file', 'dest/file');
this.fs.copy('source', 'dest');
File System Docs
Still not documented, but this is the delete method (works for me):
this.fs.delete('file/to/delete');
Link: Yeoman issue 1505

Resources