I am doing bundling and minification for javascript files. I am doing this using gulp. Now I want that if I make any change in any of my file and hit gulp then it generate a new bundled and minified file with version number like:
<script src="https://cdn.test.com/bundle-1.0.0-min.js/"></script>
then
<script src="https://cdn.test.com/bundle-1.0.1-min.js/"></script>
I want to do this using gulp because I am already using gulp for other purposes. And one more thing if this is possible then is there any way that I don't specify version no in my html page every time I make a change and my html page get the latest version by its own somehow.
This is just a rename of the file in general. But this should really not be an automated task to increment the version number. Otherwise you will be quickly getting a version like 1.0.2092 what is not helpful. I would suggest to read the version out of the package.json and use it for the name of the file. Should be pretty easy, if you already worked with gulp.
If you don't want to use the global version (version entry) of your package.json, you could add an own entry for your bundle version. Or even use a different file than package.json. You could even use that as config for which files should be bundled, to have everything in one place:
{
"bundle": {
"version": "1.0.1",
"files": [
"path/to/file-one.js",
"another/file.js",
"..."
]
}
}
Just a quick example:
var pkg = require("./package.json");
var gulp = require("gulp");
var rename = require("gulp-rename");
gulp.src(pkg.bundle.files)
.concat("bundle.js")
.pipe(uglify())
.pipe(rename(function(path) {
path.extname = "-" . pkg.bundle.version + "-min" + path.extname;
}))
.pipe(gulp.dest("./"));
Note: instead of rename you can just set the concat name, but I like to split this. But just to be complete:
.concat("bundle-" + pkg.bundle.version + "-min.js")
About the second parts of your question, to replace things in your files:
This would be possible if you build your html pages too, and replace/inject the relevant path into it. You could use the version of the package.json again, to build it and replace. Or use tools like gulp-inject. That simple tool can add js and css files into your html templates. Just create an area where they should be placed in the html file, like: <!-- inject:js --><!-- endinject -->. Afterwards it is a simple gulp taks too:
var pkg = require("./package.json");
var gulp = require("gulp");
var inject = require("gulp-inject");
gulp.src("dev/index.html")
.pipe(inject("bundle-" + pkg.bundle.version + "-min.js"))
.pipe(gulp.dest("prod/"));
Related
I have a hybrid AngularJS/Angular application that will take some time to complete migration to fully be an Angular app. While this process occurs, I'd like to move away from the previous build system to using the CLI and webpack to manage all of the old AngularJS scripts as well. This is possible as I've done it before by adding all of my scripts to the scripts section in angular.json like the following:
"scripts": [
"src/app/angularjs/app.js",
"src/app/angularjs/controllers/main.js",
"src/app/angularjs/services/someService.js",
"src/app/angularjs/controllers/someController.js"
],
This works well and the CLI builds via ng serve and ng build continue to work for the hybrid bootstrapped app as needed. The problem I'm running into now is manually listing each file for the current application I'm migrating is not ideal. I have hundreds of scripts that need to be added, and what I need is to be able to use a globbing pattern like the following:
"scripts": [
"src/app/angularjs/**/*.js"
],
The problem is this syntax from what I can tell is not supported. The glob pattern is supported in the assets section of angular.json as stated here but not in the scripts section: https://angular.io/guide/workspace-config#assets-configuration
In the scripts section I can't find a similar solution. It does have an expanded object API, but nothing that solves the problem I can tell to select all .js files from a particular directory as listed here: https://angular.io/guide/workspace-config#styles-and-scripts-configuration
Is it possible by some means to use a glob pattern or similar approach to select all files of a directory for the scripts section in angular.json so I don't have to manually list out hundreds of individual .js files?
The Bad News
The scripts section does not support the same glob patterns that the assets section does.
The Good News(?)
Since you're transitioning away from AngularJS, you hopefully won't have any new files to import in the future, so you could just generate the list of all the files you need to import.
Make your way to the src/app/angular directory and run the following:
find . -iregex '.*\.\(js\)' -printf '"%p",\n'
That will give you your list, already quoted for your convenience. You may need to do a quick search/replace (changing "." to "src/app/angularjs"), and don't forget to remove the last comma, but once you've done that once you should be all set.
The Extra News
You can further filter out unwanted files with -not, so (per your comment) you might do:
find . -iregex '^.*\.js$' -not -iregex '^.*_test\.js$' -printf '"%p",\n'
And that should give you all your .js files without your _test.js files.
KISS
Of course, this isn't a complex pattern, so as #atconway points out below, this will work just as well:
find . -iname "*.js" -not -iname "*_test.js" -printf '"%p",\n'
I'll keep the above, though, for use in situations where the full power of regex might come in handy.
I wanted to extend an anser of #JasonVerber and here is a Node.JS code and therefore (I believe) cross-platform.
Firstly install find package and then save contents from the snippet in some file.js.
Afterwards, specify paths so that they resolve to where you wan't to get your files from and where to put the resulting file to.
After that node file-name.js and this will save all found file paths to the resultPath in result.txt ready to Ctrl+A, Ctrl+C, Ctrl+V.
const find = require('find');
const path = require('path');
const fs = require('fs');
// BEFORE USAGE INSTALL `find` package
// Path to the folder where to look for files
const sourcePath = path.resolve(path.join(__dirname, 'cordova-app', 'src'));
// Path that will be removed from absolute path to files
const pathToRemove = path.resolve(path.join(__dirname, 'cordova-app'));
// Path where to put result.txt
const resultPath = path.resolve(path.join(__dirname, './result.txt'));
// Collects the file paths
const res = [];
// Path with replaced \ onto /
const pathToRemovehReplaced = pathToRemove.replace(/\\/g, '/');
// Get all fils that match a regex
find.eachfile(/\.js$/, sourcePath, file => {
// First remove all \ with / and then remove the path from root to source so that only relative path is left
const fileReplaced = file.replace(/\\/g, '/').replace(`${pathToRemovehReplaced}/`, '');
// Surround with quoutes
res.push(`"${fileReplaced}"`);
}).end(() => {
// Write file and concatenate results with newline and commas
fs.writeFileSync(resultPath, res.join(',\r\n'), 'utf8');
console.log('DONE!');
});
The result I got while testing (/\.ts$/ for regex)
"src/app/app.component.spec.ts",
"src/app/app.component.ts",
"src/app/app.module.ts",
"src/environments/environment.prod.ts",
"src/environments/environment.ts",
"src/main.ts",
"src/polyfills.ts",
"src/test.ts"
I've written a gulp task to rename files so that they can be versioned. The problem is that the filenames of the files that the index.html scripts reference are not changed.
For example, in my index.html:
<script src=pub/main_v1.js"></script>
But if you actually navigate through the build folder to the subdirectory pub, you will find main.js.
Here is the custom gulp task:
const gulpConcat = require('gulp-concat');
const gulpReplace = require('gulp-replace');
const version = require('./package.json').version;
gulp.task('version', function () {
var vsn = '_' + version + '.js';
gulp.src('scripts/**/*.js')
.pipe(gulpConcat(vsn))
.pipe(gulp.dest('./prodBuild'));
return gulp.src('./prodBuild/index.html', { base: './prodBuild' })
.pipe(gulpReplace(/* some regex */, /* append vsn */))
.pipe(gulp.dest('./prodBuild'));
});
What do I need to fix/add so that the original filename changes to match that in the script tag?
Note: According to the gulp-concat docs, I should be able to find the concated files at prodBuild/[vsn], where [vsn] is _v1.js. However, it is no where to be found.
Update: The files rename properly in index.html, but I can't seem to get the renaming of the original files to work. Here's a snapshot of my build directory:
prodBuild/
pub/
main.js
someDir/
subDirA/
// unimportant stuff
subDirB/
file2.js
file3.js
// ...other files and folders...
EDIT:
The issue is that you return only one of the two tasks. The first task is simply ignored by gulp, since it is not returned. A simple solutions: Split it into two tasks, and reference the one from the other, like in this SO answer.
Old Answer
This looks like a perfect case for the gulp-rename. You could simply pipe your scripts through gulp-rename, like this:
.pipe(rename(function (path) {
path.basename += vsn;
path.extname = ".js"
}))
Gulp concat is, AFAIK, made for the concatination of files, not particularly for the renaming of them.
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 would like to copy a list of folders to a destination with gulp
So far i've come up with a working solution, but its far from performant.
The structure of my directory is like this:
App
src
web
some files...
and i would like to copy it to
build
src
web
the files
The code i am using to accomplish this is:
var paths = [path.app + '/src/', path.app + '/app/'].concat(path.assets);
paths.forEach(function(value, index){
// value.replace(path.app, path.build);
gulp.src(value + '/**/*')
.pipe(gulp.dest(value.replace(path.app, path.build)));
});
Where the assets are my files (or other directories)
However there is a loop and no clear return value. I am wondering if there is a more performant way of doing this
I'm not sure I understand what you're trying to do here (where is your gulp task definition for example?), but it seems like you just want to copy everything below App to the build folder while preserving directory structure.
If that's the case, you don't have to loop over the files and replace folder names yourself. Gulp does it for you:
gulp.task('default', function () {
return gulp.src('App/**')
.pipe( gulp.dest('build') );
});
Everything before the ** is automatically stripped from the path of files written to build, so you end up with build/src, build/web, etc ...
Using a version of what grunt-contrib-watch recommends for compiling only changed files in here: https://github.com/gruntjs/grunt-contrib-watch#compiling-files-as-needed
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
grunt.config('jshint.all.src', Object.keys(changedFiles));
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});
This works fine (again with a variation I wrote for it here: https://gist.github.com/pgilad/6897875)
The problem is when using include inside Jade templates, meaning you are including other Jade templates in order to build the complete html file.
Using the singular solution for compile doesn't work because if a .jade file you are working on is embeded using include current_working_jade.jade - the including file won't get recompiled.
Are there any workarounds for this besides compiling all of your jade files from scratch? This causes a problem when you have around ~60 large jade files to compile every time.
The only possible solution I can think of is either mapping jade templates dependencies either externally or with directories, but I don't know any tools/plugins which do that...
After already starting to work on a scaffold that will generate a sortof jade sourcemap I found this great project, that already solves this issue:
Jade Inheritance
Usage is as follows:
Install package using: npm install jade-inheritance --save-dev
Where you want to get a list of dependent files from a jade:
var JadeInheritance = require('jade-inheritance');
var inheritance = new JadeInheritance(file, basedirname, {basedir:basedirname});
Then when you want to get the file:
depenedentFiles = inheritance.files;
The project also demonstrates how to apply the concept with grunt.watch in order to compile only changed jade files with their dependents, exactly what I needed:
Using jade-inheritance with grunt watch
I imagine something like checking all jade files and if they include your changed file then recompile that as well. Shouldn't be too hard. Pseudo code:
var allFiles = getAllJadeFileWithIncludesAndProjectPathMap();
//allFiles now contains something like this
{
'jade/index.jade': ['jade/menu.jade', 'jade/home.jade'],
'jade/about.jade': ['jade/menu.jade']
}
var rootFiles = [];
_.each(allFiles, function (includes, parent) {
_.each(includes, function (includePath) {
var parent;
while (parent = getParentPath(includePath)) {
//nothing needed if getParentPath returns null for files that aren't included
}
if (rootFiles.indexOf(parent) !== -1) {
rootFiles.push(parent);
}
});
});
Now add these files to the compile task.