Grunt:递归模板如何工作?

时间:2014-01-14 18:32:40

标签: javascript node.js gruntjs lodash

我是Grunt的新手,我在使用递归模板时遇到了一些问题。这是一个具体的,最小的例子:

var path = require('path');

module.exports = function(grunt) {
  grunt.initConfig({
    // Stash path here so we can reference it from templates.
    path: path,
    argPrint: function(arg1, arg2) { return "arg1: " + arg1 + " arg2: " + arg2; },
    build: {
      root_dir: __dirname,
      build_dir: '<%= path.resolve(build.root_dir, "dev") %>',
      vendor_dir: '<%= path.resolve(build.build_dir, "vendor") %>',
      classes_dir: '<%= path.resolve(build.vendor_dir, "classes") %>',
      test: '<%= argPrint(build.build_dir, "vendor")  %>'
    }
  });

  grunt.registerTask('print_build_dir', 'Prints the build directory.', function() {
    grunt.log.writeln(grunt.config("build.root_dir"));
    grunt.log.writeln(grunt.config("build.build_dir"));
    grunt.log.writeln(grunt.config("build.vendor_dir"));
    grunt.log.writeln(grunt.config("build.classes_dir"));
    grunt.log.writeln(grunt.config("build.test"));
  });
};

将其弹出到Gruntfile.js,运行npm install grunt,然后运行grunt print_build_dir

如果您在/Users/jvilk/Code/grunt-test,我期待输出将是:

$ grunt print_build_dir
/Users/jvilk/Code/grunt-test
/Users/jvilk/Code/grunt-test/dev
/Users/jvilk/Code/grunt-test/dev/vendor
/Users/jvilk/Code/grunt-test/dev/vendor/classes
arg1: /Users/jvilk/Code/grunt-test/dev arg2: vendor

相反,输出是:

$ grunt print_build_dir
/Users/jvilk/Code/grunt-test
/Users/jvilk/Code/grunt-test/dev
/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/dev/vendor
/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/dev/vendor/classes
arg1: /Users/jvilk/Code/grunt-test/dev arg2: vendor

这里发生了什么?从test配置属性来看,显然path.resolve应该收到vendor_dir属性的正确参数 - 即它会解析为path.resolve("/Users/jvilk/Code/grunt-test/dev", "vendor") - 但我完全是混淆了为什么它将build.root_dir的值加上额外的正斜杠给该属性。

我和我挣扎的Gruntfile都会非常感谢任何帮助或指导。谢谢!

编辑:作为先发制人的附录,我意识到我不需要使用模板来实现这个特定的例子 - 我可以直接使用path.resolve。但是,在我的非示例Gruntfile中,path.resolve中的一个目录名是动态设置的属性,需要使用模板。

EDIT2:正如Andrew指出的那样,模板扩展可以通过以下方式在vendor_dir上运行:

'<%= path.resolve(build.build_dir, "vendor") %>' ->
  path.resolve('<%= path.resolve(build.root_dir, "dev") %>', "vendor") ->
    '/Users/jvilk/Code/grunt-test/<%= path.resolve(build.root_dir, "dev") %>/vendor' ->
      '/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/vendor'

我的test属性有效,因为它扩展如下:

'<%= argPrint(build.build_dir, "vendor")  %>' ->
  argPrint('<%= path.resolve(build.root_dir, "dev") %>', "vendor") ->
    'arg1: <%= path.resolve(build.root_dir, "dev") %> arg2: vendor' ->
       'arg1: /Users/jvilk/Code/grunt-test arg2: vendor'

我不明白他们为什么做出这个设计决定,但我可以通过将所有这些包装在一个重复处理字符串的函数中直到所有<%=都消失来轻松模拟我想要的模板语义。

2 个答案:

答案 0 :(得分:2)

首先让我从一个工作样本开始。

  grunt.initConfig({
    path: path,
    argPrint: function(arg1, arg2) { return "arg1: " + arg1 + " arg2: " + arg2; },
    resolvePath: function(from, to) { return path.resolve(grunt.config.process(from), to); },
    build: {
      root_dir: __dirname,
      build_dir: "<%= resolvePath(build.root_dir, 'dev')%>",
      vendor_dir: "<%= resolvePath(build.build_dir, 'vendor') %>",
      classes_dir: "<%= resolvePath(build.vendor_dir, 'classes') %>",
      test: "<%= argPrint(build.build_dir, 'vendor')  %>"
    }
  });

  grunt.registerTask('print_build_dir', 'Prints the build directory.', function() {
    grunt.log.writeln(grunt.config.get("build.root_dir")); 
    grunt.log.writeln(grunt.config.get("build.build_dir")); 
    grunt.log.writeln(grunt.config.get("build.vendor_dir"));  
    grunt.log.writeln(grunt.config.get("build.classes_dir"));
    grunt.log.writeln(grunt.config.get("build.test"));
  });
};

这会输出您的期望:

  

/用户/ andrewtremblay /开发/咕噜测试

     

/用户/ andrewtremblay /开发/咕噜-测试/开发

     

/用户/ andrewtremblay /开发/咕噜测试的/ dev /供应商

     

/ Users / andrewtremblay / Development / grunt-test / dev / vendor / classes

     

arg1:/ Users / andrewtremblay / Development / grunt-test / dev arg2:vendor

我使用ResolvePath函数测试时注意到的是传递给from的值未被处理。 (而不是使用config.get('build.build_dir')的输出,它使用的是config.getRaw('build.build_dir')

的结果

我不知道这是不是一个bug,但我认为答案在于path.resolve以及如何传递可处理的变量会导致未定义的行为。

From the docs(强调我的意思):

  

path.resolve([from ...], to)

     

如果to不是绝对的from参数,则前缀为。{   左边的顺序,直到找到绝对路径。 如果使用全部来自   路径仍然没有找到绝对路径,当前的工作目录   也被使用。生成的路径被标准化,并且尾随   除非将路径解析为根,否则将删除斜杠   。目录

那(加上path.resolve没有处理你的配置对象的事实)可以解释为什么你的工作目录不断前置。

答案 1 :(得分:0)

当Grunt从get执行config时,如果找到模板模式,则会处理这些值。

看起来它会处理模板,直到找到没有模板模式的字符串。因此,您所处的每一步都是:

__dirname =>
  '/Users/jvilk/Code/grunt-test'

build.build_dir =>
  '<%= path.resolve(build.root_dir, "dev") %> =>
    __dirname + "dev" =>
      '/Users/jvilk/Code/grunt-test/dev'

build.vendor_dir =>
  '<%= path.resolve(build.build_dir, "vendor") %>' =>
    '/Users/jvilk/Code/grunt-test/<%= path.resolve(build.root_dir, "dev") %>/vendor' =>
      '/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/dev/vendor'

build.classes_dir =>
  '<%= path.resolve(build.vendor_dir, "classes") %>' =>
    '/Users/jvilk/Code/grunt-test/<%= path.resolve(build.build_dir, "vendor") %>'/classes' =>
      '/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/<%= path.resolve(build.root_dir, "dev") %>/vendor/classes' =>
        '/Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test//Users/jvilk/Code/grunt-test/dev/vendor/classes'

除了在运行时手动扩展配置外,我不知道是否有一个好办法:

grunt.config("build.build_dir", grunt.config("build.build_dir"))
grunt.config("build.vendor_dir", grunt.config("build.vendor_dir"))
grunt.config("build.classes_dir", grunt.config("build.classes_dir"))