JavaScriptで1クラス=1ファイルでコードを書いていると .js ファイルが沢山できる。

これを一々、<script> タグにして HTML に埋め込むのが以外に面倒くさい。

で、ant の fileset で何とかならないかと思って調べてみたらこうなった。

<taskdef resource="net/sf/antcontrib/antcontrib.properties">
  <classpath>
    <pathelement location="${project.root}/lib/ant-contrib-1.0b3.jar"/>
  </classpath>
</taskdef>

<target name="main">
    <antcall target="makeListFile">
        <param name="includes" value="**/*.js"/>
        <param name="file" value="html.txt"/>
        <param name="prefix" value="&lt;script src='"/>
        <param name="suffix" value="'/>"/>
    </antcall>
</target>

<target name="makeListFile">
    <fileset id="fileset" dir=".">
        <include name="${includes}" />
    </fileset>

    <pathconvert property="files" refid="fileset" pathsep=";" dirsep="/" >
        <map from="${basedir}/" to="" />
    </pathconvert>

    <echo file="${file}"></echo>
    <foreach target="addLine" param="name" inheritall="true"
        list="${files}" delimiter=";"
    >
   </foreach>
</target>
<target name="addLine">
        <echo append="true" file="${file}">${prefix}${name}${suffix}
</echo>
</target>

面倒くせー。 そもそも contrib ずっとベータだし。

次に script タグを試して見ようと思って見付けたのがこちらのサンプル。

scriptタスクのマニュアル より

<?xml version="1.0" encoding="ISO-8859-1"?>
<project name="MyProject" basedir="." default="main">

  <property name="fs.dir" value="src"/>
  <property name="fs.includes" value="**/*.txt"/>
  <property name="fs.excludes" value="**/*.tmp"/>

  <target name="main">
    <script language="javascript"> <![CDATA[

      // import statements
      // importPackage(java.io);
      importClass(java.io.File);

      // Access to Ant-Properties by their names
      dir      = project.getProperty("fs.dir");
      includes = MyProject.getProperty("fs.includes");
      excludes = self.getProject()  .getProperty("fs.excludes");

      // Create a <fileset dir="" includes=""/>
      fs = project.createDataType("fileset");
      fs.setDir( new File(dir) );
      fs.setIncludes(includes);
      fs.setExcludes(excludes);

      // Get the files (array) of that fileset
      ds = fs.getDirectoryScanner(project);
      srcFiles = ds.getIncludedFiles();

      // iterate over that array
      for (i=0; i<srcFiles.length; i++) {

        // get the values via Java API
        var basedir  = fs.getDir(project);
        var filename = srcFiles[i];
        var file = new File(basedir, filename);
        var size = file.length();

        // create and use a Task via Ant API
        echo = MyProject.createTask("echo");
        echo.setMessage(filename + ": " + size + " byte");
        echo.perform();
      }
    ]]></script>
  </target>
</project>

もっと面倒くせー orz

結局、自前でスクリプトを呼ぶTask書くのが早いんじゃね、と思って作ったのがこれ。

package jstask;

import java.io.File;
import java.util.*;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;
import org.apache.tools.ant.types.resources.FileResource;
import org.mozilla.javascript.*;

public class JsTask extends Task  {
    private List<ResourceCollection> rclist = new ArrayList<ResourceCollection>();
    private List<Parameter> params = new ArrayList<Parameter>();
    private String text;

    public void add(ResourceCollection rc) {
        rclist.add(rc);
    }
    public void addParam(Parameter param) {
        params.add(param);
    }
    public void addText(String str) {
        text = str;
    }

    @Override
    public void execute() throws BuildException {
        Context cx = Context.enter();
        try {
            Scriptable scope = cx.initStandardObjects();
            scope.put("task", scope, this);
            scope.put("fileset", scope, toFileList(rclist));
            for (Parameter param : params) {
                scope.put(param.getName(), scope, param.getValue());
            }
            cx.evaluateString(scope, text, "ant.js", 0, null);
        } finally {
            Context.exit();
        }
    }   

    private List<File> toFileList(List<ResourceCollection> rclist) {
        List<File> filelist = new ArrayList<File>(); 
        for (ResourceCollection rc : rclist) {
            for (Iterator<?> ite = rc.iterator(); ite.hasNext();) {
                Resource resource = (Resource) ite.next();
                if (resource instanceof FileResource) {
                    File file = ((FileResource) resource).getFile();
                    filelist.add(file);
                }
            }
        }
        return filelist;
    }
}

使用例:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project basedir="." default="test" name="test">
    <taskdef name="js"
        classname="jstask.JsTask"
        classpath="lib/jstask.js;lib/js.jar"
    />
    <target name="test">
        <js>
            <fileset dir="src" includes="**/*.js" />
            <param name="prefix" value="^${basedir}/" />
            <param name="outfile" value="html.txt" />

            <![CDATA[
            var out = new java.io.FileWriter(outfile);
            var ite = fileset.iterator();
            while (ite.hasNext()) {
                var reg = new RegExp(prefix);
                var fname = (""+ite.next()).replace(reg,"");
                out.write("<script src='"+fname+"'></script>\n");
            }
            out.close();
            ]]>
        </js>
    </target>   
</project>

結局これが一番簡単だった...