package com.fitbank.modularjs; import java.io.*; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.yahoo.platform.yui.compressor.YUICompressor; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.util.FileUtils; import org.codehaus.plexus.util.IOUtil; public class ModularJsBuilder { private static final Pattern INCLUDE_REGEX = Pattern.compile("^(\\s*)include\\(['\"](.*)['\"]\\);\r?$"); public static Log log; public static final Set EXCLUDE_CONCAT = new HashSet(); private List INCLUDED = new LinkedList(); private List INCLUDING = new LinkedList(); public static File module2file(File baseDir, String name, boolean mobile) { return new File(baseDir, name.replace(".", File.separator) + (mobile ? "-mobile" : "") + ".js"); } public static String file2module(File baseDir, File file) { return baseDir.toURI().relativize(file.toURI()).getPath().replaceAll(".js$", "").replace(File.separator, "."); } private ModularJsBuilder() { } public static void build(File rootDir, File outputDir) throws IOException { buildDir(rootDir, outputDir, rootDir); } private static void buildDir(File rootDir, File outputDir, File currentDir) throws IOException { for (File file : currentDir.listFiles(new FileFilter() { @Override public boolean accept(File pathname) { return pathname.isDirectory() || (pathname.getName().endsWith(".js") && !pathname.getName().endsWith(".compressed.js") && !pathname.getName().endsWith(".build.js")); } })) { if (file.isDirectory()) { buildDir(rootDir, outputDir, file); } else { new ModularJsBuilder().buildFile(rootDir, outputDir, file, false); new ModularJsBuilder().buildFile(rootDir, outputDir, file, true); } } } private void buildFile(File rootDir, File outputDir, File file, boolean mobile) throws IOException { StringWriter out = new StringWriter(); processFile(rootDir, new BufferedReader(new FileReader(file)), out, false); String compiledJs = out.toString(); File build = new File(outputDir, file2module(rootDir, file) + (mobile ? ".build-mobile.js" : ".build.js")); FileWriter buildFw = new FileWriter(build); IOUtil.copy(compiledJs, buildFw); buildFw.close(); File compressed = new File(outputDir, file2module(rootDir, file) + (mobile ? ".build.compressed-mobile.js" : ".build.compressed.js")); try { YUICompressor.main(new String[]{build.getAbsolutePath(), "-o", compressed.getAbsolutePath()}); } catch (Exception e) { log.error(" [E] Cannot compress " + build.getAbsolutePath() + ": " + e.getLocalizedMessage()); FileUtils.copyFile(build, compressed); } } private void processFile(File rootDir, BufferedReader in, Writer out, boolean mobile) throws IOException { processFile(rootDir, in, out, "", mobile); } private void processFile(File rootDir, BufferedReader in, Writer out, String indent, boolean mobile) throws IOException { String line; while ((line = in.readLine()) != null) { Matcher m = INCLUDE_REGEX.matcher(line); if (m.find()) { String module = m.group(2); if (!include(rootDir, out, module, m.group(1), mobile)) { out.write(indent + line); } } else { out.write(indent + line); } out.write(System.getProperty("line.separator")); } } private boolean include(File rootDir, Writer out, String module, String indent, boolean mobile) throws IOException { if (INCLUDED.contains(module)) { return true; } if (EXCLUDE_CONCAT.contains(module)) { INCLUDED.add(module); return false; } if (INCLUDING.contains(module)) { throw new IOException("Recursive import of module: " + module); } File moduleFile = ModularJsBuilder.module2file(rootDir, module, mobile); if (mobile && !moduleFile.exists()) { moduleFile = ModularJsBuilder.module2file(rootDir, module, false); } if (!moduleFile.exists()) { log.debug(" [E] Module " + module + " not found"); return false; } INCLUDING.add(module); out.write("\nif (!modularjs.loaded[\"" + module + "\"]) {\n"); processFile(rootDir, new BufferedReader(new FileReader(moduleFile)), out, indent, mobile); out.write("\n}\n"); INCLUDING.remove(module); out.write("\nmodularjs.loaded[\"" + module + "\"] = true;\n"); INCLUDED.add(module); log.debug(" [L] Module " + module + " loaded"); return true; } }