Hope someone can help me figure out this.
I have 3 products that have almost the same interface, lets say Product A, B and C
What I want to do, if possible(through Gulp/Grunt/Other), is to create one source code and when building the final app, it will carry over to the dist folder the part for Produtc A, B C.
Example:
app/
js/
assets/
views/
dist/
ProdA/
ProdB/
ProdC/
Creating the build isn't the hard part, but how can I make my code so I can consume consume different API for each product.
Example:
Product A, B and C have status pages, A have (3 fields), B have all field from A (plus one) and C have fields completely different.
Do you guys have tips/tricks/guides so I can accomplish a similar task and reduce my code re-use?
Some Thoughts and Options
Your question seems to leave it to interpretation. Too many open ended answers possible, and many great answers will be provided. However, based on what I think you are doing, here are some options:
Use an external config file, and use globbing patterns for each, and exclude what you don't want.
Create a gulp task that copies some source files and moves them to your dist/a, dist/b, etc.
Turn your reusable code into a node_module and 'require' it in each apps src.
Turn your reusable code into a git submodule and use it as a dependency for each app. Works well with the previous point.
A Gulp Task to move files and folders
For exactly what you asked, all sharing one repo (I do highly recommend separate repos though to simplify the tasks), here is a gulp task you can use to move files around. It's very simple.
This example / suggestion will have two parts for convenience. The task, and a config section to manage it. You can either use it in the gulpfile, or as an external file which may be easier to manage if you are using the same gulpfile for three projects.
You could also use merge-stream to do the same task to multiple src & dest:
npm install --save-dev merge-stream
gulpfile.js
// =========================================================
// Projects: Project A, Project B, Project C
// info:
//
// =========================================================
// ------------------------------------------------ Requires
var gulp = require('gulp'),
merge = require('merge-stream');
// -------------------------------------------------- Config
var config = {
// These are some examples
reusable: [
'./src/reusableCode/scripts/**/*.js',
'./src/reusableCode/styles/**/*.css
],
productA: {
src: [ ], // Add product A src
opts: { }, // Add product A options for gulp tasks,
dest: './dist/A'
},
productB: {
src: [ ], // Add product B src
opts: { }, // Add product B options for gulp tasks,
dest: './dist/B'
},
productC: {
src: [ ], // Add product C src
opts: { }, // Add product C options for gulp tasks,
dest: './dist/C'
},
}
// --------------------------------------------------- Tasks
// ...
gulp.task('moveSrc', function(){
var prodA = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/A' ) );
var prodB = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/B' ) );
var prodC = gulp.src( config.reusable )
.pipe( gulp.dest( './dist/C' ) );
return merge( prodA, prodB, prodC);
});
// A task to move folders and files without `merge-stream`
// gulp.task( 'moveSingleStream', function() {
// gulp.src takes the src into a stream, and output the stream
// just by using gulp.dest . This moves files and folders around
gulp.src( config.reusable )
.pipe( './dist' );
});
// --------------------------------------------------- Build
gulp.task( 'build', [ 'moveSrc', 'otherTask1', 'otherTask2' ] );
You could also skip the reasuable array in config in this example, and just add what you want to move in each products src array. Whatever works.
Now that being said, I would suggest separate repo's and using that reusable code as node modules, but you can use merge-stream to simplify the process of using the same tasks to do multiple things. I hope this helps.
Assuming I understand your question, what you want is a basic service that has modifications by the product type. If it is the case, you can use angular's service decorator, this enables you to take an existing service and add to it functionality, either by defining new methods or wrapping the behavior of existing methods. If this works for you, have each product decorate the base service according to its needs.
There are many examples of decorations, for example this one.
Related
When I minified my css, I was left with an incorrect path to the fonts from various libraries. So, I created a task to move the fonts from my bower_components/ folder to dist/public/fonts:
gulp.task('doit', function() {
gulp.src(["public/bower_components/bootstrap/dist/fonts/*", "public/bower_components/font-awesome/fonts/*"])
.pipe(gulp.dest("dist/public/fonts"));
});
Basically that should throw any fonts I need into a generic fonts folder, which my minified css should now be able to access.
But after I run it, dist/public/fonts doesn't exist. Why not?
I don't fully understand the paths you're src-ing (public/bower_components?), but I believe you'll want to use the base option for gulp.src.
Because these two globs will have different bases, I'd suggest breaking it into two separate tasks, and building a third to aggregate them into a single. Otherwise you'll need to get into merging streams or the addSrc plugin.
gulp.task('copy:fonts:bootstrap', function () {
return gulp.src(
[
'public/bower_components/bootstrap/dist/fonts/**/*'
],
{
base: 'public/bower_components/bootstrap/dist/fonts'
}
)
.pipe(gulp.dest('dist/public/fonts'));
});
gulp.task('copy:fonts:fontawesome', function () {
return gulp.src(
[
'public/bower_components/font-awesome/fonts/**/*'
],
{
base: 'public/bower_components/font-awesome/fonts'
}
)
.pipe(gulp.dest('dist/public/fonts'));
});
gulp.task('copy:fonts', ['copy:fonts:bootstrap', 'copy:fonts:fontawesome']);
According to this article, specify your src like this:
gulp.src(['src/js/**/*.js'], { base: 'src' })
.pipe(foo())
.pipe(gulp.dest("./public/"));
and it will auto create the destination directories for you. In this case, the 'js' folder will be created in public if it doesnt exist already.
I'm using gulp to build a single javascript file with gulp-concat and gulp-uglify.
Original Files
//File 1
var Proj = Proj || {};
//File 2
Proj.Main = (function() {
var Method = function(){ /*Code*/ };
return { "Method":Method };
})();
//File 3
Proj.Page = (function() {
var Method = Proj.Main.Method;
return { "Method":Method };
})();
Gulp returns a bad minified file because these files are being concatenated in the wrong order. I know I can specify the order in .src([]) but I don't want to maintain the array as I add javascript files.
Is there a way to create references to these "namespaces" without having to worry about the order of the files concatenated? Or, is there a way for gulp to handle concatenation with the knowledge of these namespaces auto-magically?
EDIT:
I know I can specify the file order inside the .src([]). I want to develop without having to worry about the file order, whether it be through a gulp package or a javascript framework. Thank you for responses that help but I need a definitive "No. You cannot do this." or "Yes. Here's how..." to mark the thread as answered.
Well, one option is to try gulp-order.
Also, check out this answer to "gulp concat scripts in order?".
Basically, it mentions what you already said, about having to explicitly name the files in the order you want them to come in. I know you don't want to do that, but how else would gulp know which order you want your files in?
One thing worth pointing out, though, is that you have a group of files where the order doesn't matter, and then, say, 2 files where the order does matter, you can do something like this:
gulp.src([
'utils/*.js',
'utils/some-service.js',
'utils/something-that-depends-on-some-service'
])
gulp-concat doesn't repeat files, so everything that's not some-service.js or something-that-depends-on-some-service.js will get concatenated first, and then the last two files will be concatenated in the proper order.
Since it hasn't been mentioned, implementing webpack or browserify will absolutely solve this problem without implementing some sort of hacky feeling solution.
Here is a simple example of how to use it:
var source = require('vinyl-source-stream'), //<--this is the key
browserify = require('browserify');
function buildEverything(){
return browserify({
//do your config here
entries: './src/js/index.js',
})
.bundle()
.pipe(source('index.js')) //this converts to stream
//do all processing here.
//like uglification and so on.
.pipe(gulp.dest('bundle.js'));
}
}
gulp.task('buildTask', buildEverything);
And inside your files you use require statements to indicate which files require others.
Is there a portable way to get at the currently loaded Gruntfile?
grunt.registerTask('someTask', 'someTask in a separate grunt instance', function() {
var gruntFile = getCurrentGruntFile(); // ???
var done = this.async();
var child = grunt.util.spawn({
cmd: "grunt",
args: ['someTask', '--gruntfile=' + gruntFile, '-v'],
}, done);
});
I needed to run jshint in a subshell because it was outputting color codes into the reporter-outputFile. I imagine that there should really only be one Gruntfile per project, but setting a Gruntfile dynamically could be useful when developing grunt plugins or during initial configuration.
Grunt.config only lists task-level options. The documentation for Inside Tasks doesn't list anything useful. Grunt.util doesn't list anything relevant either.
EDIT
I have already set-up the jshint task using grunt.util.async to spawn a separate grunt in a subshell and pass it the --no-colors CLI arg. I was simultaneously developing several different Gruntfiles for this particular project and I couldn't figure out a way to determine the currently loaded Gruntfile. Fortunately, it was just a curiosity and this jshint task works fine if you have only one Gruntfile or don't need to change it. Also, if it was a project requirement to have several Gruntfiles I imagine they could be kept in different directories and then the grunt tasks could be initiated in the relevant directory.
grunt.registerTask('jshintNoColor', 'jshint without color-code output', function() {
var done = this.async();
var child = grunt.util.spawn({
cmd: "grunt",
args: ['jshint', '--no-color'],
opts: {
stdio: 'inherit'
}
}, done);
});
So here is my hypothetical config object for a hypothetical fooTask that does something (not relevant to question) to a bunch of JS files
grunt.initConfig({
fooTask: {
app1: 'app1/*.js',
app2: 'app2/*.js',
app3: 'app3/*.js'
}
});
As you can see, with this approach, I have to run fooTask 3 times with each app specified as a target:
grunt fooTask:app1
grunt fooTask:app2
grunt fooTask:app3
Needless to say this does not scale as either the number of apps increase or the number of such foo tasks increase as one has to C&P the same code over and over for each app.
So ideally what I would like to define is just one target with the name of the app passed in as a config variable
grunt.initConfig({
fooTask: {
dist: '<%=appName%>/*.js'
}
});
I would then like to call fooTask 3 times, one for each app, with the right app set as appName
var apps = ['app1', 'app2', 'app3'];
apps.forEach(function(app) {
var currAppName = app;
// Run fooTask but how do I specify the new currAppName config?
grunt.task.run('fooTask');
});
As from code above, I know I can run my fooTask using grunt.task.run but how do I set the appName config for my task?
Note that this question is similar to this other one that also does not have the right answer yet - Pass Grunt config options from task.run
Thanks a lot.
EDIT 2:
So nevermind the garbage below the first edit, leaving as example of what doesn't work. In my case it was really important to be able to set the value within a task at run-time so I settled on the file system. Perhaps it suits your needs.
grunt.initConfig({
someTask: {
someKey: fs.readFileSync('file.txt', { encoding: 'utf8' })
}
});
of course you can do the readFile outside of the task if you need a bunch of different app names.
EDIT:
Hmmm. I swear I had this working when I wrote this...but now it is not. Grunt just sees the extra arguments as additional unfound tasks.
I was trying to figure this out myself, finally a "duh" just moment happened - why not parse process.argv before grunt.initConfig?
module.exports = function(grunt) {
var sourcefile = process.argv[2] || 'default.js'; // <- this
grunt.initConfig({
uglify: {
main: {
src: sourcefile, // <- voila :)
dest: sourcefile.substring(0, sourcefile.length-3) + '.min.js'
}
}
});
grunt.loadNpmTasks('uglify');
grunt.registerTask('default', ['uglify']);
};
and use from command line:
grunt mykillerscript.js
I didn't even try to use grunt.option for the same reason that all the examples only showed directing which task is run, but I wouldn't be surprised if there is a more "grunt" way to do this.
I am currently using Grunt, and as I was trying Gulp, the same problem I encountered first with Grunt occurred to me.
I am trying to process some js files (concat, uglify and minify them), but I don't want all of them to compile into one big file, I want multiple output files, each from the processing of some input files :
scripts =
firstOutput:
outputFilename: 'first.min.js',
inputFiles: ['one.js', 'two.js']
secondOutput:
outputFilename: 'second.min.js',
inputFiles: ['three.js']
thirdOutput:
outputFilename: 'third.min.js',
inputFiles: ['four.js', 'five.js']
The only way I found (for now) to achieve that with Grunt is with multiple watches and multiple uglify tasks (or one uglify task and a listener on watch change to dynamically modify the uglify task src and dest) :
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-contrib-watch'
grunt.loadNpmTasks 'grunt-contrib-uglify'
grunt.initConfig
watch:
firstOutput:
files: scripts.firstOutput.inputFiles
tasks: ['uglify:firstOutput']
options :
spawn : false
secondOutput:
files: scripts.secondOutput.inputFiles
tasks: ['uglify:secondOutput']
options :
spawn : false
thirdOutput:
files: scripts.thirdOutput.inputFiles
tasks: ['uglify:thirdOutput']
options :
spawn : false
uglify:
firstOutput:
files: scripts.firstOutput.inputFiles
dest: scripts.firstOutput.outputFilename
secondOutput:
files: scripts.secondOutput.inputFiles
dest: scripts.secondOutput.outputFilename
thirdOutput:
files: scripts.thirdOutput.inputFiles
dest: scripts.thirdOutput.outputFilename
grunt.registerTask 'default', 'watch'
And, as you can imagine, this is just an example, in my case of a big web application, there's a lot more than just three output js files, and I also process a few less files into some css files
My Gruntfile is really huge, and I find it has a lot of duplicate code, is there any way to have this code refactored to have one watch and one uglify task, with an automatically guessed src and dest with some kind of dependency (to know that if the four.js file is modified, it has to process the third output) ?
If you have some way to do it with Gulp I'll take it with great pleasure, as I would like to test it in my usual workflow.
Here's how you can do this with gulp + vanilla javascript:
var _ = require("underscore")
, gulp = require("gulp")
, uglify = require("gulp-uglify")
var scripts = [
{
output: 'first.min.js',
input: ['one.js', 'two.js']
}
, {
output: 'second.min.js',
input: ['three.js']
}
, {
output: 'third.min.js',
input: ['four.js', 'five.js']
}
];
function build(files, dest) {
return gulp.src(files)
.pipe(uglify())
.pipe(gulp.dest(dest));
}
gulp.task("watch", function () {
_.each(scripts, function (script, i) {
gulp.watch(script.input, function () {
build(script.input, script.output);
});
});
});
Even better if you can use globs to match sets of files so you don't have to write out the path for every single input set. Something like input: ["one/**/*.js, "other/**/*.js"]
"I am trying to process some js files (concat, uglify and minify
them), but I don't want all of them to compile into one big file"
Can I ask why? The benefit of one larger file is that you save on HTTP requests, every resource you load will cause some slowdown of your website. May I suggest using proper dependency management with RequireJS? That way the optimiser can walk your dependency graph and output optimised files for you.
http://requirejs.org/
There's a grunt task for this too:
https://github.com/gruntjs/grunt-contrib-requirejs