sindresorhus / gulp-rev

Static asset revisioning by appending content hash to filenames: `unicorn.css` → `unicorn-d41d8cd98f.css`
MIT License
1.54k stars 218 forks source link

read manifest information without writing a file #125

Closed colthreepv closed 8 years ago

colthreepv commented 8 years ago

I'm having the need to read the collected rev.manifest content, without writing it as a .json file.

That because I want to revision all my static content files and then pass the manifest to a templating engine like nunjucks, that will interpolate the variables in the HTML files, creating revisioned URLs.

How is that achievable?

bobthecow commented 8 years ago

rev.manifest() doesn't write anything to disk, it just generates a vinyl "file" and passes it to the next thing in your pipeline. If you give an example of how you want to use it, we might be able to help :)

colthreepv commented 8 years ago

Sure, after reading the library code I don't think it will ever work for my use-case, but I will post an example nonetheless:

// index page is a nunjucks template
gulp.task('index', function () {
  return gulp.src('index.j2')
    .pipe(data(rev.manifestData))
    .pipe(nunjucks())
    .pipe(gulp.dest(destDir));
});

gulp.task('less', function () {
    return gulp.src('css/*.less')
      .pipe(less({
        paths: [
          path.join(process.cwd(), 'node_modules')
        ]
      }))
      .pipe(rev())
      .pipe(gulp.dest(destDir));
});

gulp.task('code', function () {
    return b.bundle()
      .pipe(source('bundle.js'))
      .pipe(buffer())
      .pipe(rev())
      .pipe(gulp.dest(destDir));
});

// gulp 4 syntax
gulp.task('build', gulp.series(
  gulp.parallel('clean'),
  gulp.parallel('less', 'static-files', 'code'),
  gulp.series('index')
));

In this example , calling gulp build will process files in a parallel way memorizing revision informations in a global object, that is then accessed via the index task, without the need to store it as a file.

I will probably start a fork and see where I will head towards

ghost commented 8 years ago

I want to do the same thing. I dynamically create one gulp task per bundle. In my bundle pipeline, after I call .pipe(rev()) I tried calling .pipe(rename(collectBundleFilename(bundleName))) using gulp-rename.

My main 'html' task is dependent on the dynamically created bundle tasks and the collectBundleFilename is setup like this:

var bundleFileNames = {};

function collectBundleFilename(bundleName) {
  function innerFnCalledByGulpRename(file) {
    // Save the filename for this bundle. We know all the rest of the info about it already.
    bundleFileNames[bundleName] = file.basename + file.extname;
  }
  return innerFnCalledByGulpRename;
}

When I log calls to innerFnCalledByGulpRename, I can see that I'm getting the new filename with the rev hash. However, when my 'html' task runs, there seems to be only one bundle path in bundleFileNames. So, perhaps I'm doing something else wrong (in terms of running gulp tasks sequentially). Maybe I'll try using gulp-series because specifying a dependency order is not working.

UPDATE: I'm on gulp 3.9.0 btw.

ghost commented 8 years ago

@colthreepv The solution that I outlined above now works after I utilized run-sequence, which you should not need since it appears that you're using Gulp 4.

I happened to use the gulp-rename plugin to call my collectBundleFilename function and get the info I needed, but if you need more info from the actual vinyl file you can just write your own gulp plugin function to get it (or find a gulp plugin that lets you look at each vinyl file as it passes through...or maybe there is some gulp api that I don't know about).

ghost commented 8 years ago

Here's my log. The issue I had before plugging in run-sequence was that all my debug messages would come out of order. Now you can see that everything is running in order.

'js-bundle-nguibs' and 'js-bundle-main' are the two dynamically created tasks that I created with lodash.forEach(bundlesObj, function(bundle, name) { gulp.task('js-bundle-' + name, ....

dynamically created tasks:
[ 'clean',
  'images',
  'css',
  'js-bundle-nguibs',
  'js-bundle-main',
  'html' ]
[01:57:58] Using gulpfile D:\devoptix\staticbuild-demo-jade\gulpfile.js
[01:57:58] Starting 'default'...
[01:57:58] Starting 'clean'...
[01:57:58] Finished 'clean' after 14 ms
[01:57:58] Starting 'images'...
[01:57:58] Finished 'images' after 26 ms
[01:57:58] Starting 'css'...
[01:57:58] Finished 'css' after 36 ms
[01:57:58] Starting 'js-bundle-nguibs'...
[01:58:02] gulpBundledJs bundle: nguibs result: /lib/nguibs/scripts-cfe16c964f.js
[01:58:02] Finished 'js-bundle-nguibs' after 3.88 s
[01:58:02] Starting 'js-bundle-main'...
[01:58:02] gulpBundledJs bundle: main result: /lib/main/scripts-5191973edb.js
[01:58:02] Finished 'js-bundle-main' after 8.55 ms
[01:58:02] Starting 'html'...
Outputting bundles...
Outputting bundles...
Outputting bundles...
[01:58:02] Finished 'html' after 122 ms
[01:58:02] Finished 'default' after 4.1 s
colthreepv commented 8 years ago

Your suggestion is very clever and does not require me to mantain a fork of the project, thank you so much @waynebloss.

For my use case this is a bit simplistic, as in your function you can only track 1 file per each collectBundleFilename If I gulp.src a directory with many css files and want to track all the revision obtained, we need to rework the function.

I will work on it, in the meantime I created a really simple fork that covers mine (and supposedly) more use-cases without the need to add more dependencies

I will report here if I get a more elegant solution

colthreepv commented 8 years ago

Found a more elegant solution, without the need to include forcefully gulp-rename, or to fork gulp-rev

rev-hash.js

'use strict';
var
  path = require('path'),
  stream = require('stream');

var revData = {}; // exposed hash
function trackRevision (file, unused, callback) {
  // use this to check what's happening
  // console.log(path.relative(file.revOrigBase, file.revOrigPath), '->', path.relative(file.base, file.path));
  revData[path.relative(file.revOrigBase, file.revOrigPath)] = path.relative(file.base, file.path);
  callback(null, file);
}

exports = module.exports = function () {
  var passthrough = new stream.Transform({ objectMode: true });
  passthrough._transform = trackRevision;
  return passthrough;
};
exports.data = revData;

Usage is like this:

gulp.task('code', function () {
    return b.bundle()
      .pipe(source('bundle.js'))
      .pipe(buffer())
      .pipe(rev())
      .pipe(revHash())
      .pipe(gulp.dest(destDir));
});

// index page is a nunjucks template
gulp.task('index', function () {
  console.log('Injecting into templates', revHash.data);
  return gulp.src('index.j2')
    .pipe(data(revHash.data))
    .pipe(nunjucks())
    .pipe(gulp.dest(destDir));
});

// gulp 4 syntax
gulp.task('build', gulp.series(
  gulp.parallel('clean'),
  gulp.parallel('less', 'static-files', 'code'),
  gulp.series('index')
));

Hope this is helpful to others, maybe I will turn it into a micro plugin for gulp-rev

ghost commented 8 years ago

@colthreepv Making your own plugin is definitely more robust than using gulp-rename. Thanks for sharing your code, I might use that.

ghost commented 8 years ago

Something like gulp-fncallback might work too. Any gulp plugin that lets you look at the actual vinyl file should work I think.

torifat commented 8 years ago

@colthreepv seems like this issue is solved. Can we close this?

colthreepv commented 8 years ago

Sure thing, using this small solution in production since months ;)