proper way of copying files to destination with gulp - asp.net

I'm toying around with ASP.NET 5 and am using gulp. I added angularjs and angular-route to my package.json file which stored the files at Dependencies->NPM. I added this to my gulpfile.js thinking that it would copy over the the correct JS files. It did copy over the files, however, it also crashed the project. I had to manually go into the lib folder and remove everything that gulp added. What's the proper way to copy files from the NPM folder a destination folder. I'd like to be able to just run the task from Task Runner.
I'm assuming this is incorrect: (which is what I ran)
gulp.task("copyJs", function () {
return gulp.src('./node_modules/**/*.js')
.pipe(gulp.dest('./wwwroot/lib/'))
});

*I think the trailing '/' in gulp.dest('./wwwroot/lib/') might be the cause of the problem, try gulp.dest('./wwwroot/lib') instead.
This is the gulp workflow I use for Angular 2 with Asp.Net 5.
var gulp = require("gulp"),
merge = require("merge-stream"),
rimraf = require("rimraf");
var paths = {
webroot: "./wwwroot/",
node_modules: "./node_modules/"
};
paths.libDest = paths.webroot + "lib/";
gulp.task("clean:libs", function (cb) {
rimraf(paths.libDest, cb);
});
gulp.task("copy:libs", ["clean:libs"], function () {
var angular2 = gulp.src(paths.node_modules + "angular2/bundles/**/*.js")
.pipe(gulp.dest(paths.libDest + "angular2"));
var es6_shim = gulp.src([
paths.node_modules + "es6-shim/*.js",
"!**/Gruntfile.js"])
.pipe(gulp.dest(paths.libDest + "es6-shim"));
var systemjs = gulp.src(paths.node_modules + "systemjs/dist/*.js")
.pipe(gulp.dest(paths.libDest + "systemjs"));
var rxjs = gulp.src(paths.node_modules + "rxjs/bundles/**/*.js")
.pipe(gulp.dest(paths.libDest + "rxjs"));
return merge(angular2, es6_shim, systemjs, rxjs);
});

There are many ways to do it but one of the good simple ways I found was this: http://www.hanselman.com/blog/ControlHowYourBowerPackagesAreInstalledWithAGulpfileInASPNET5.aspx
Which do an update to the bowerrc file and everything after this update makes more sense.
UPDATE YOUR .BOWERRC AND PROJECT.JSON
In the root of your project is a .bowerrc file. It looks like this:
> { "directory": "wwwroot/lib" } Change it to something like this, and
> delete your actual wwwroot/lib folder.
>
> { "directory": "bower_components" } EXCLUDE YOUR SOURCE BOWER FOLDER
> FROM YOUR PROJECT.JSON
You'll also want to go into your project.json file for ASP.NET 5 and
make sure that your source bower_components folder is excluded from
the project and any packing and publishing process.
> "exclude": [
> "wwwroot",
> "node_modules",
> "bower_components" ],
UPDATE YOUR GULPFILE.JS
In your gulpfile, make sure that path is present in paths. There are
totally other ways to do this, including having gulp install bower and
figure out the path. It's up to you how sophisticated you want your
gulpfile to get as long as the result is that production ready .js
ends up in your wwwroot ready to be served to the customer. Also
include a lib or destination for where your resulting JavaScript gets
copied. Could be scripts, could be js, could be lib as in my case.
var paths = {
webroot: "./" + project.webroot + "/",
bower: "./bower_components/",
lib: "./" + project.webroot + "/lib/" }; ADD A COPY TASK TO YOUR GULPFILE
Now open your Gulpfile and note all the tasks. You're going to add a
copy task to copy in just the files you want for deployment with your
web app.
Here is an example copy task:
> gulp.task("copy", ["clean"], function () {
> var bower = {
> "bootstrap": "bootstrap/dist/**/*.{js,map,css,ttf,svg,woff,eot}",
> "bootstrap-touch-carousel": "bootstrap-touch-carousel/dist/**/*.{js,css}",
> "hammer.js": "hammer.js/hammer*.{js,map}",
> "jquery": "jquery/jquery*.{js,map}",
> "jquery-validation": "jquery-validation/jquery.validate.js",
> "jquery-validation-unobtrusive": "jquery-validation-unobtrusive/jquery.validate.unobtrusive.js"
> }
>
> for (var destinationDir in bower) {
> gulp.src(paths.bower + bower[destinationDir])
> .pipe(gulp.dest(paths.lib + destinationDir));
> } });
Do note this is a very simple and very explicit copy tasks. Others
might just copy more or less, or even use a globbing wildcard.
It's up to you. The point is, if you don't like a behavior in ASP.NET
5 or in the general build flow of your web application you have more
power than ever before.
Right click the Bower node in the Solution Explorer and "Restore
Packages." You can also do this in the command line or just let it
happen at build time.
Looking in this simplified screenshot, you can see the bower
dependencies that come down into the ~/bower_components folder. Just
the parts I want are moved into the ~/wwwroot/lib/** folder when the
gulpfile runs the copy task.

I manage very complex monorepos, I don't like hardcoded file paths and prefer to mirror my source code for transparency. I explored a LOT of solutions for doing a lot of files at once and find them all opaque and bloated. I recommend a factory that ultimately does this with source-like file module references:
gulp.parallel(
() =>
gulp
.src(require.resolve('#bootstrap/core/dist/bootstrap.all.min.js'))
.pipe(gulp.dest(DIST)),
() =>
gulp
.src(require.resolve('foobar/dist/foobar.all.min.js'))
.pipe(gulp.dest(DIST))
You can make them named functions for visibility as well.

Related

How to use grunt to copy a series of files to the same directory they come from?

I have a series of directories like...
component A
- t9n
- componentAT9n.json
component B
- t9n
- componentBT9N.json
Where each of these directories I need to duplicate the one file ending in t9n.json to _en.json ultimately ending up with...
component A
- t9n
- componentAT9n.json
- componentAT9n_en.json
component B
- t9n
- componentBT9N.json
- componentBT9n_en.json
Using grunt is how I'm trying to do it, but I'm having a hard time figuring out how to copy each file it matches to the same directory.
Is this something I can accomplish just using grunt and grunt-contrib-copy ? Or is there maybe another plugin to do this?
In the copy task, I know I can use glob patterns to dynamically grab the source, but the destination I'm unsure of.
files: [
{
cwd: "src/app/js",
src: ["**/t9n/*.json"],
dest: ???,
expand: true
}
]
This situation ended up being just too complicated that I had to make a custom grunt task for it, this is what I did.
// Copies the root t9n file for each t9n directory to an _en.json file
grunt.registerTask("create-en-t9n-files", function() {
grunt.file
.expand("path/to/files/**/t9n/t9n+([A-Za-z0-9]).json")
.forEach(function(source) {
var destinationPath = source.split("/t9n/")[0] + "/t9n/";
var fileName = source.split("/t9n/")[1].split(".json")[0];
var enName = fileName + "_en.json";
grunt.file.copy(source, destinationPath + enName);
});
});

Grunt relative file path globbing

Is it possible to use Globbing partially on a directory in a file path?
I have a grunt-contrib-less task set up, the file path for my task looks something like this:
files: {
"../../application/user/themes/some-theme-5.1.1.5830/css/main.css": "less/base.less",
}
However the version number in the relative path may sometime change, such as:
files: {
"../../application/user/themes/some-theme-5.1.1.5831/css/main.css": "less/base.less",
}
Ideally I'd like to something like this:
files: {
"../../application/user/themes/some-theme-*/css/main.css": "less/base.less",
}
Is there a way of doing this? With the above syntax it stops searching after the asterisk.
One potential solution to achieve this is to utilize grunts --options feature.
When running a grunt task via the command line it is possible to specify an additional options value.
In your scenario you could pass in the version number of the folder name that is going to change. (I.e. In your case the part that you tried to specify using the asterisk character (*) E.g. '5.1.1.5830'
Caveat: For this solution to be of any use it does require knowing what that value, (the version number), of the destination folder is upfront prior to running the task via the command line.
Example Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
themesFolder: {
namePart: '0.0.0.0' // <-- If no option is passed via the CLI this name will be used.
},
less: {
production: {
options: {
// ...
},
files: {
// The destination path below utilizes a grunt template for the part
// of the folder name that will change. E.g. '5.1.1.0'
'../../application/user/themes/some-theme-<%= themesFolder.name %>/css/main.css': 'less/base.less'
}
}
}
});
grunt.loadNpmTasks('grunt-contrib-less');
grunt.registerTask('saveFolderNameFromOption', 'Uses the option provided to configure name part.', function(n) {
var themesFolder = grunt.option('themesFolder');
if (themesFolder) {
// Store the option value so it can be referenced in the less task.
grunt.config('themesFolder.namePart', themesFolder);
}
});
grunt.registerTask('processLess', ['saveFolderNameFromOption', 'less:production']);
};
Running the ProcessLess task
Run the task via the command line as follows:
$ grunt processLess --themesFolder=5.1.1.5830
Note: The additional option that is specified. Namely: --themesFolder=5.1.1.5830
When using the above command the .css output will be directed to the following path:
'../../application/user/themes/some-theme-5.1.1.5830/css/main.css': 'less/base.less'
Now, each time you run the task you modify the options accordingly.
Benefits: By providing the version number as an option via the CLI will avoid having to reconfigure your Gruntfile.js each time it is run.

Customize semantic-ui using Bower and Grunt

My project uses Bower to install deps and Grunt to build. My project tree looks like this
|
|-bower_components
| |
| |-jquery
| |-semantic
| |-...
|-Bower.json
|-Gruntfile.js
|-public
| |
| |-css // Compiled, concatenated and minified semantic-ui
| |-js // and other libs should be here
|-...
|-etc..
Is it possible to build custom semantic-ui (ie customize fonts, colors, remove unused components) using Grunt (or maybe using Gulp called from Grunt)?
Where to place semantic theme config and overrides files?
It's not difficulty to use grunt to build semantic-ui. I don't know about bower, but this is how I did it.
Install grunt-contrib-less.
Create a new directory somewhere in your project, e.g. '/less/semantic'. Copy 'site' directory from your semantic packagea, i.e. 'bower_components/semantic/src/site' to the new directory. All your overrides will be done here.
Create a config.json file in '/less/semantic' to configure what components you want to be included in your build. The file content will be something like this:
{
"elements": ["button", "divider"],
"collections": ["form"],
"modules": ["checkbox"]
}
Add following to your gruntFile.js file:
var fs = require('fs');
// Defines files property for less task
var getSemanticFiles = function() {
var files = {};
var config = JSON.parse(fs.readFileSync('less/semantic/config.json'));
var srcDir = 'bower_components/semantic/definitions/';
var outputDir = 'less/semantic/output/';
for (var type in config) {
config[type].forEach(function(ele) {
files[outputDir + type + '.' + ele + '.output'] = [srcDir + type + '/' + ele + '.less'];
});
}
return files;
};
Configure less task as following:
less: {
semantic: {
options: { compile: true }
files: getSemanticFiels()
},
dist: {
options: { compile: true }
files: { 'public/css/semantic.css': ['less/semantic/output/*'] }
}
}
Edit theme.config in 'bower_components/semantic/src', change #siteFoler to '../../../less/site/', and make any additional changes as needed per semantic document.
You run grunt less:semantic to compile all needed components, and then run less:dist to put them into a single css file.
Of course you can configure a watch task to automate the process. Then every time you make a change, the css will be automaticly re-built.
I am sure someone will build a grunt build to semantic one day, but for now, I just use this to call all the gulp commands using grunt. https://github.com/sindresorhus/grunt-shell. Just make sure you are calling the gulp build task and not the default gulp task. It has a watch task that will cause grunt to not finish the shell task.

How can I skip a grunt task if a directory is empty

I'm using grunt-contrib's concat and uglify modules to process some javascript. Currently if src/js/ is empty, they will still create an (empty) concat'd file, along with the minified version and a source map.
I want to task to detect if the src/js/ folder is empty before proceeding, and if it is, then the task should skip (not fail). Any ideas how to do this?
The solution may not be the prettiest, but could give you an idea. You'll need to run something like npm install --save-dev glob first. This is based on part of the Milkshake project you mentioned.
grunt.registerTask('build_js', function(){
// get first task's `src` config property and see
// if any file matches the glob pattern
if (grunt.config('concat').js.src.some(function(src){
return require('glob').sync(src).length;
})) {
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});
A gist for comparison: https://gist.github.com/kosmotaur/61bff2bc807b28a9fcfa
With this plugin:
https://www.npmjs.org/package/grunt-file-exists
You can check file existence. (I didn't try, but the source looks like supporting grunt expands. (*, ** ...)
For example like this::
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js']
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
'maintask',
]);
But maybe if the file doesn't exist it will fail with error instead of simple skip.
(I didn't test it.)
If this is a problem you can modify a bit the source of this plugin to run the related task if the file exists:
The config:
grunt.initConfig({
fileExists: {
scripts: ['a.js', 'b.js'],
options: {tasks: ['maintask']}
},
});
grunt.registerTask('conditionaltask', [
'fileExists',
]);
And you should add this:
grunt.task.run(options.tasks);
In this file:
https://github.com/alexeiskachykhin/grunt-file-exists/blob/master/tasks/fileExists.js
after this line:
grunt.log.ok();
Maybe this is just a more up-to-date answer as the others are more than a year old, but you don't need a plugin for this; you can use grunt.file.expand to test if files matching a certain globbing pattern exist.
Update of #Kosmotaur's answer (path is just hard-code here though for simplicity):
grunt.registerTask('build_js', function(){
// if any file matches the glob pattern
if (grunt.file.expand("subdir/**/*.js").length) { /** new bit here **/
// if so, run the task chain
grunt.task.run([
'trimtrailingspaces:js'
, 'concat:js'
, 'uglify:yomama'
]);
}
});

How do I process multiple files as templates with yeoman generator?

I'm working on a custom generator that I originally wrote with grunt-init. One difference I'm noticing is grunt-init automatically processes all the files in root as templates but with yeoman generator you have to do this using .template(). I'm familiar with how to process any individual file with .template() but is it possible to process an entire directory?
This issue is an annoying one. I came across this when I used yeoman for the first time. I think the below code snippet can help you.
this.directory('scripts', 'scripts');// script is folder name
Looking at the Yeoman generator code, there doesn't seem to be a built in way to do this. The way I solved this was to copy some of the code from the built in generator code and modify it for my needs. I determine if a file is a template based on the _ prefix convention since I want to rename the files to exclude that prefix, but you could just treat every file as a template and it would work fine. This will copy all of the files in the directory, so what I also did is to exclude the .DS_STORE files that you find by default on OSX, but since that is a specific case I didn't include that here.
require('path');
MyGenerator.prototype._processDirectory = function(source, destination) {
var root = this.isPathAbsolute(source) ? source : path.join(this.sourceRoot(), source);
var files = this.expandFiles('**', { dot: true, cwd: root });
for (var i = 0; i < files.length; i++) {
var f = files[i];
var src = path.join(root, f);
if(path.basename(f).indexOf('_') == 0){
var dest = path.join(destination, path.dirname(f), path.basename(f).replace(/^_/, ''));
this.template(src, dest);
}
else{
var dest = path.join(destination, f);
this.copy(src, dest);
}
}
};
Yeoman uses mem-fs-editor, which has support for glob patterns. However the documentation is not very clear, and you may miss that point. Here is the documentation of copyTpl, that says it accepts the same options as copy. So since copy has suppport for glob patterns, copyTpl too.
At any point on your yeoman generator you can do:
const from = 'myFolder/**.js'
const to = 'project/'
this
.fs
.copyTpl(
this.templatePath(from),
this.destinationPath(to),
this.props, {interpolate: /<%=([\s\S]+?)%>/g}
);
},
Not that, if you are using a glob pattern the destination path should be a folder.

Resources