Dynamic file mapping with relative destination

2019-06-09 21:06发布

问题:

This is a generic question about dynamic file mapping with Grunt.js, but for the example purpose, I'll be trying to build coffee files in a project with a dynamic structure :

Here, I can have multiple (dynamic) target folders with a different depth. Finding coffee files remain easy, it will match **/coffee/*.coffee anytime.

What I'm trying to achieve, is making the dest property relative to the matched coffee file :

  • find **/coffee/*.coffee
  • compile to ../js/*.js

Instead of making it relative to the project folder (Gruntfile).

coffee: {
    compile: {
        files: [
            {
              expand: true,
              src: ['**/coffee/*.coffee'],
              dest: '../js/', // This won't work ! But I wish it could :)
              ext: '.js'
            }
        ]
    }
}

How would you achieve this ?

回答1:

I don't think that's a very good idea. It sounds like each target should perhaps be a repository of its own. Even if we ignore that, you'll have other problems, such as difficulty telling target and non-target folders apart. For example, your current pattern would potentially match CoffeeScript files in the node_modules/ directory.

I also find it weird that your targets have differing folder structures (i.e. coffee/ and js/ are not on the same level). Again, it sounds like they're different projects, and should have their own repos.

That being said, if you really, really have to do it like that, there are a few ways to accomplish this.

First, the "normal" way to do it would be to simply specify multiple targets manually. I'll be using the Gruntfile.coffee syntax here:

coffee:
  target1:
    expand: true
    cwd: 'target1/coffee'
    src: '**/*.coffee'
    dest: 'target1/js'
    ext: '.js'
  targetX:
    expand: true
    cwd: 'targetX/some-folder/coffee'
    src: '**/*.coffee'
    dest: 'targetX/some-folder/js'
    ext: '.js'

However if you're certain you need to have dynamic targets, and don't mind blacklisting unwanted folders, perhaps try something like this:

coffee: do ->
  targets = {}
  for target in grunt.file.expand '**/coffee', '!node_modules/**'
    targets[target.split('/', 1)[0]] =
      expand: true
      cwd: target
      src: '**/*.coffee'
      dest: target + '/../js'
      ext: '.js'
  targets

This will essentially do the same as the previous snippet, with some added uncertainty. You could perhaps get rid of the blacklist by having a targets/ folder contain all the targets, and using targets/**/coffee as the pattern.

Alternatively you could use grunt.file.expandMapping:

coffee:
  compile:
    files: grunt.file.expandMapping ['**/coffee/**/*.coffee', '!node_modules/**'], '',
      expand: true
      ext: '.js'
      rename: (base, src) ->
        src.replace '/coffee/', '/js/'

While it may look kind of simple (and ugly), it's the slowest option and feels the most wrong.

So there. It's possible, but most likely not what you really want.