I am trying to write a gradle task which will minify all my project's javascript files. I am using a gradle library: com.eriwen.gradle.js. This library contains a task called minifyJs where we define the source file we want to minify and the destination of the minified file:
minifyJs {
source = file(sourcePathString)
dest = file(targetPathString)
}
What I want to do is call execute this task for EVERY javascript file in my project and produce a minified version of it in a new path for EACH file. This would require me to run the minifyJs task multiple times each time with different source and dest values, but I can't seem to find a solution on how to do this. One person had suggested that we use a loop to create a new task of type: minifyJs for each javascript file but this takes a huge amount of time and will create 250+ tasks i.e. not effective at all.
Since calling a task inside another task doesn't work (and using task.execute() is bad practice) I'm essentially looking for a workaround that lets me achieve this:
task customMinify {
def jsFileTree = fileTree('my/javascript/files')
jsFileTree.forEach {
def jsFile = it
minifyJs {
source = file(jsFile.getPath())
dest = file('new/path/to/file.js')
}
}
}
which obviously doesn't work since we can't call minifyJs inside another task.
I'm really sorry that this gap has continued to exist in the gradle-js-plugin.
Since generating tasks won't do, I suggest that you write a custom task under buildSrc combining my JsMinifier and the MinifyJsTask.
If you're willing to wait 8 hours or so, I can write an implementation of this later if you like.
EDIT: Here's a gist for a ClosureMinifyTask you can throw in buildSrc/src/main/groovy/com/eriwen/gradle/js/tasks and it'll minify each file individually and produce individual source map files etc.
buildSrc/build.gradle:
repositories {
mavenCentral()
}
dependencies {
compile localGroovy()
compile gradleApi()
compile ('com.google.javascript:closure-compiler:v20151015') {
exclude module: 'junit'
}
}
Sample Usage:
task mini(type: com.foo.bar.ClosureMinifyTask) {
source = "src/js"
dest = "${buildDir}/js/minified"
}
Related
The project I'm working on is using combineJS from this gradle plugin in its gradle build. What it does is combine the contents of a directory, in this case topsoil_js, into a single javascript file. I would like to do the same, but with the contents of a different directory, plots_js, into a different single file.
The current code looks like this:
javascript.source {
topsoil {
js {
srcDir 'src/main/resources/org/cirdles/topsoil/plot/topsoil_js'
include '*.js'
}
}
plots {
js {
srcDir 'src/main/resources/org/cirdles/topsoil/plot/plots_js'
include '*js'
}
}
}
combineJs {
source = javascript.source.topsoil.js.files
dest = file("${project.buildDir}/resources/main/org/cirdles/topsoil/plot/topsoil.js")
}
I've tried adding an identical combineJs statement to combine plots_js, like so:
combineJs {
source = javascript.source.plots.js.files
dest = file("${project.buildDir}/resources/main/org/cirdles/topsoil/plot/plots.js")
}
The problem is that source and dest get overwritten, so only the last combineJs you call actually combines a file. I'm relatively new to gradle, so I'm not actually sure how the rest of the project consumes these variables. Are they keywords that gradle knows to look for, or are they arbitrary?
Most importantly: does anyone have suggestions on how to combine both sets of files?
EDIT: I also tried following the instructions in this documentation for the plugin, which tells me to format it like so:
task combineTopsoilJs(type: com.eriwen.gradle.js.tasks.CombineJsTask) {
source = javascript.source.topsoil.js.files
dest = file("${project.buildDir}/resources/main/org/cirdles/topsoil/plot/topsoil.js")
}
task combinePlotJs(type: com.eriwen.gradle.js.tasks.CombineJsTask) {
source = javascript.source.plots.js.files
dest = file("${project.buildDir}/resources/main/org/cirdles/topsoil/plot/BasicPlot.js")
}
But this format isn't working either, just throwing an error I'm having trouble tracking down.
This seems like a very simple question, but spent the last 3 hours researching it, discovering it can be slow on every save on a new file if not using watchify.
This is my directory tree:
gulpfile.js
package.json
www/
default.htm
<script src="toBundleJsHere/file123.js"></script>
toBundletheseJs/
componentX/
file1.js
componentY/
file2.js
componentZ/
file3.js
toPutBundledJsHere/
file123.js
Requirements.
On every creation or save of a file within the folder toBundleTheseJs/ I want this file to be rebundled into toBundleJsHere/
What do I need to include in my package.json file?
And whats the minimum I need to write into my gulp file?
This should be as fast as possible so think I should be using browserify and watchify. I want to understand the minimum steps so using package manager like jspm is overkill a this point.
thanks
First you should listen to changes in the desired dir:
watch(['toBundletheseJs/**/*.js'], function () {
gulp.run('bundle-js');
});
Then the bundle-js task should bundle your files. A recommended way is gulp-concat:
var concat = require('gulp-concat');
var gulp = require('gulp');
gulp.task('bundle-js', function() {
return gulp.src('toBundletheseJs/**/*.js')
.pipe(concat('file123.js'))
.pipe(gulp.dest('./toPutBundledJsHere/'));
});
The right answer is: there is no legit need for concatenating JS files using gulp. Therefore you should never do that.
Instead, look into proper JS bundlers that will properly concatenate your files organizing them according to some established format, like commonsjs, amd, umd, etc.
Here's a list of more appropriate tools:
Webpack
Rollup
Parcel
Note that my answer is around end of 2020, so if you're reading this in a somewhat distant future keep in mind the javascript community travels fast so that new and better tools may be around.
var gulp = require('gulp');
var concat = require('gulp-concat');
gulp.task('js', function (done) {
// array of all the js paths you want to bundle.
var scriptSources = ['./node_modules/idb/lib/idb.js', 'js/**/*.js'];
gulp.src(scriptSources)
// name of the new file all your js files are to be bundled to.
.pipe(concat('all.js'))
// the destination where the new bundled file is going to be saved to.
.pipe(gulp.dest('dist/js'));
done();
});
Use this code to bundle several files into one.
gulp.task('scripts', function() {
return gulp.src(['./lib/file3.js', './lib/file1.js', './lib/file2.js']) //files separated by comma
.pipe(concat('script.js')) //resultant file name
.pipe(gulp.dest('./dist/')); //Destination where file to be exported
});
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.
I've written a function which I'd like to use as a Grunt task. I can do this by adding this to the Gruntfile:
grunt.registerTask('foo', function () {
// code here
});
However, it makes more sense to keep the function code in a separate file. I plan to define a bunch of these custom tasks and I don't want to bloat the Gruntfile.
I'm not sure what the preferred way of registering such tasks is. I have found this to work:
grunt.registerTask('foo', function () {
require('./path/to/foo.js')(grunt);
});
So, I'm having the inline function like in the fist example, but this time, I'm loading an external file and invoking it immediately. In that external file, I of course have to write:
module.exports = function (grunt) {
// code here
}
This works, but it feels hackish. Is there a more proper way of doing this?
Short answer: the alternative to this
grunt.registerTask('foo', function () {
require('./path/to/foo.js')(grunt);
});
is http://gruntjs.com/api/grunt#grunt.loadtasks
Long answer:
Normally when you have tasks in external files there are served as other nodejs modules. So, if that is something that you will use in several projects you may want to register it in the registry. Later inside your Gruntfile.js you will have:
grunt.loadNpmTasks('yout-module-here');
The grunt's documentation says:
Load tasks from the specified Grunt plugin. This plugin must be installed locally via npm, and must be relative to the Gruntfile
However, if you don't want to upload anything to the registry you should use loadTasks
grunt.loadTasks('path/to/your/task/directory');
So, once the task is loaded you may use it in your configuration.
Here is a simple grunt task placed in external file:
'use strict';
module.exports = function(grunt) {
grunt.registerMultiTask('nameoftask', 'description', function() {
var self = this;
// this.data here contains your configuration
});
};
And later in Gruntfile.js
grunt.initConfig({
nameoftask: {
task: {
// parameters here
}
}
});
I had a similar problem.
I wanted to modularize my grunt config and custom tasks by functionnalities (big UX/UI blocks) rather than by technical features. AND I wanted to keep the config files next to task files... (better when working on a large legacy codebase with an varied team - 5 persons with varying JS knowledge)
So I externalized my tasks like Krasimir did.
In the gruntfile, I wrote :
//power of globbing for loading tasks
var tasksLocations = ['./grunt-config/default_tasks.js', './grunt-config/**/tasks.js'];
var taskFiles = grunt.file.expand({
filter: "isFile"
}, tasksLocations);
taskFiles.forEach(function(path) {
grunt.log.writeln("=> loading & registering : " + path);
require(path)(grunt);
});
You will find the whole boilerplate gruntfile here (external config and tasks loading) : https://gist.github.com/0gust1/7683132
Background: I have a multi-project Gradle build, and I've defined a Gradle task which runs JavaScript unit tests in an Exec task. The inputs to this task are the JavaScript files in the project, so it's only re-run if one of the source files are modified. The task is added to all JavaScript projects from a master project.
Question: I want to extend this so that the tests are re-run if JavaScript files in the project, or in any of its project dependencies are changed. How is this best done?
The code below works if placed in each subproject build file (after the dependency declaration), but we have 20+ JavaScript subprojects and I'd like to stay DRY.
project.ext.jsSourceFiles = fileTree("src/").include("**/*.js*")
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files resolveJavascriptDependenciesFor(project)
outputs.file "report.xml"
// Run tests in JSTestDriver using command line call...
}
def resolveJavascriptDependenciesFor(project) {
def files = project.jsSourceFiles
project.configurations.js.allDependencies.each {
files = files + resolveJavascriptDependenciesFor(it.dependencyProject)
}
return files
}
Is there a better solution? Maybe where I don't have to resolve all file dependencies myself?
As written in the answer before, adding the jsTest task within a subprojects closure would make it very easy to add jstesting support for every subproject. I think you can ease your inputs setup by declaring source files as dependencies:
dependencies {
js filetree("src/main").include("**/*.js")
}
and
subprojects { subproj ->
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files subproj.configurations.js
outputs.file "report.xml"
commandLine ...
}
}
Would it be possible to do something like this?
allprojects {project ->
task testJavaScript(type: Exec, dependsOn: configurations.js) {
inputs.files resolveJavascriptDependenciesFor(project)
// Run tests in JSTestDriver using command line call...
}
}
def resolveJavascriptDependenciesFor(project) {
def files = project.jsSourceFiles
project.configurations.js.allDependencies.each {
files = files + resolveJavascriptDependenciesFor(it.dependencyProject)
}
return files
}
Tha way the task is on all projects, and wil be called recursively.
Not completely sure this works, but I think its the way to go
I've found a solution that works but isn't great, using the same dependency specification as in the question. It's to load the gradle files in a slightly different order in the master project.
Master build.gradle:
subprojects {
configurations {
js
}
// Apply the subproject's build.gradle, but with another name (that isn't automatically loaded)
if (project.file('depends.gradle').exists()) {
apply from: project.file('depends.gradle')
}
apply from: project.parent.file('javaScriptProjectTasks.gradle')
}
Compare to the previous, non working master build.gradle
subprojects {
configurations {
js
}
apply from: project.parent.file('javaScriptProjectTasks.gradle')
// The subproject's build.gradle is automatically loaded
}