jakejs / jake

JavaScript build tool, similar to Make or Rake. Built to work with Node.js.
http://jakejs.com
Apache License 2.0
1.97k stars 190 forks source link

not clear from docs how to do something like `npm install` only when necessary #320

Closed jedwards1211 closed 7 years ago

jedwards1211 commented 8 years ago

I want to only run npm install when my node_modules contents are older than my package.json and npm-shrinkwrap.json. It seems like this is the way to get jake to do what I want:

file('package.json')
file('npm-shrinkwrap.json')
file('node_modules', ['package.json', 'npm-shrinkwrap.json'], function () {
    jake.exec('npm i', {interactive: true}, complete)
}, {async: true})

Does passing a directory to file actually check if that directory and its contents are older than package.json and npm-shrinkwrap.json? The docs say nothing about passing directories to file tasks, but on the other hand they don't say anything about a task that outputs a directory, and directory doesn't appear to allow you to run anything, it just creates a prerequisite (that doesn't even seem to check the date of any of its contents).

mde commented 7 years ago

Sorry for the long delay responding to this.

This is interesting -- the file task correctly doesn't care anything about directories. They're just containers for files. (Note also Git doesn't care about directories, for the same reason.)

What you're talking about here is sort of the opposite of the purpose of a file task. It's traditionally thought of as a way to build one file out of many source files. You're talking about a process that creates many files if one (or two, in this case) has been touched.

You could pretty easily write a Jake task that does exactly what you want here, something like this:

let fs = require('fs');
let execSync = require('child_process').execSync;
let exec = function (cmd) {
  return execSync(cmd, {stdio: 'inherit'});
};
task('npmInstall', function () {
  let list = new jake.FileList('./node_modules/**/package.json');
  let modTimes = l.toArray().map((p) => { return fs.statSync(p).mtime; });
  let maxModTime = Math.max.apply(null, modTimes);
  let needsInstall = [
    fs.statSync('package.json').mtime,
    fs.statSync('npm-shrinkwrap.json').mtime
  ].some((t) => { return t > maxModTime; });
  if (needsInstall) {
    exec('npm i');
  }
});

As a side note, now that we have sync shell ability in Node, I'm a lot less likely to want to use jake.exec for anything. :)

mde commented 7 years ago

Again, sorry for the lengthy delay in responding to this issue. I hope this gets things working for you.

jedwards1211 commented 7 years ago

@mde hi, thanks for your response! Honestly I didn't end up using jake in the long term. I basically ended up doing what you described with the mod times in my own script though. Good point about only checking the package.json though -- right now I'm checking all files!

If you're curious what's involved in my build process, check out https://github.com/jcoreio/crater/tree/master/scripts.

I'm surprised this kind of need never came up among jake users. I can imagine an elegant syntax for indicating this kind of thing.