Exemplo n.º 1
0
  /**
   * Writes out all of the module files. This method is only applicable when modules are used. This
   * is expected to be used only with the build command.
   *
   * @throws IOException
   */
  public void writeCompiledCodeToFiles(
      final Function<String, String> moduleNameToUri, String sourceMapPath) throws IOException {
    if (modules == null) {
      throw new IllegalStateException("This compilation does not use modules");
    }

    ModuleConfig moduleConfig = config.getModuleConfig();
    Map<String, File> moduleToOutputPath = moduleConfig.getModuleToOutputPath();
    final Map<String, String> moduleNameToFingerprint = Maps.newHashMap();
    final boolean isDebugMode = false;
    for (JSModule module : modules) {
      String moduleName = module.getName();
      File outputFile = moduleToOutputPath.get(moduleName);
      com.google.common.io.Files.createParentDirs(outputFile);

      // Reset the source map if it is not going to be reset later in this
      // loop
      // when the source map is written to disk.
      final boolean resetSourceMap = (sourceMapPath == null);
      String moduleCode =
          getCodeForModule(moduleName, isDebugMode, moduleNameToUri, resetSourceMap);

      // Fingerprint the file, if appropriate.
      if (config.shouldFingerprintJsFiles()) {
        String fileName = outputFile.getName();
        String fingerprint = Md5Util.hashJs(moduleCode);
        moduleNameToFingerprint.put(moduleName, fingerprint);
        fileName = insertFingerprintIntoName(fileName, fingerprint);
        outputFile = new File(outputFile.getParentFile(), fileName);
      }

      Files.write(moduleCode, outputFile);

      // It turns out that the SourceMap will not be populated until after
      // the
      // Compiler's internal representation has been output as source
      // code, so
      // it should only be written out to a file after the compiled code
      // has
      // been generated.
      if (sourceMapPath != null) {
        Writer writer = Streams.createFileWriter(sourceMapPath + "_" + moduleName, config);
        // This is safe because getCodeForModule() was just called,
        // which has
        // the side-effect of calling compiler.toSource(module).
        SourceMap sourceMap = compiler.getSourceMap();
        sourceMap.appendTo(writer, moduleName);
        sourceMap.reset();
        Closeables.close(writer, false);
      }
    }

    if (moduleConfig.excludeModuleInfoFromRootModule()) {
      File outputFile = moduleConfig.getModuleInfoPath();
      com.google.common.io.Files.createParentDirs(outputFile);

      final Function<String, String> fingerprintedModuleNameToUri =
          new Function<String, String>() {
            @Override
            public String apply(String moduleName) {
              String uri = moduleNameToUri.apply(moduleName);
              String fingerprint = moduleNameToFingerprint.get(moduleName);
              if (fingerprint != null) {
                uri = insertFingerprintIntoName(uri, fingerprint);
              }
              return uri;
            }
          };

      Writer writer = Streams.createFileWriter(outputFile, config);
      appendRootModuleInfo(writer, isDebugMode, fingerprintedModuleNameToUri);
      Closeables.close(writer, false);
    }
  }
Exemplo n.º 2
0
  /**
   * This method always calls compiler.toSource(module), which means that it can be followed by a
   * call to compiler.getSourceMap().appendTo(writer, module), though
   * compiler.getSourceMap().reset() should be called immediately after when that is the case.
   *
   * <p>This method is sychronized in order to fix
   * http://code.google.com/p/plovr/issues/detail?id=31. The problem is that if two modules are
   * requested at the same time, it is often the case that compiler.toSource(module) is executing
   * for the second module while the source map is still being modified for the first module,
   * causing an IllegalStateException. Empirically, synchronizing this method appears to fix things,
   * though a more provably correct (and minimal) solution should be sought.
   */
  private synchronized String getCodeForModule(
      String moduleName,
      boolean isDebugMode,
      Function<String, String> moduleNameToUri,
      boolean resetSourceMap) {
    Preconditions.checkState(hasResult(), "Code has not been compiled yet");
    Preconditions.checkState(modules != null, "This compilation does not use modules");

    StringBuilder builder = new StringBuilder();
    ModuleConfig moduleConfig = config.getModuleConfig();
    String rootModule = moduleConfig.getRootModule();

    boolean isRootModule = rootModule.equals(moduleName);

    if (isRootModule) {
      // For the root module, prepend the following global variables:
      //
      // PLOVR_MODULE_INFO
      // PLOVR_MODULE_URIS
      // PLOVR_MODULE_USE_DEBUG_MODE
      //
      // Because the standard way to read these variables in the
      // application is:
      //
      // moduleLoader.setDebugMode(!!goog.global['PLOVR_MODULE_USE_DEBUG_MODE']);
      // moduleManager.setLoader(moduleLoader);
      // moduleManager.setAllModuleInfo(goog.global['PLOVR_MODULE_INFO']);
      // moduleManager.setModuleUris(goog.global['PLOVR_MODULE_URIS']);
      //
      // It is important that the PLOVR variables are guaranteed to be
      // global,
      // which (as much as it pains me) is why "var" is omitted.
      if (!moduleConfig.excludeModuleInfoFromRootModule()) {
        try {
          appendRootModuleInfo(builder, isDebugMode, moduleNameToUri);
        } catch (IOException e) {
          // This should not occur because data is being appended to
          // an
          // in-memory StringBuilder rather than a file.
          throw new RuntimeException(e);
        }
      }
    }

    int lineOffset = 0;

    JSModule module = nameToModule.get(moduleName);

    StringBuffer prependBuffer = new StringBuffer();
    if (isRootModule) {
      lineOffset++; // one line for the function wrapper
      for (File prepend : config.getPrependInputs()) {
        try {
          String s = Files.toString(prepend);
          if (s != null) {
            String[] lines = s.split("\n");
            for (String line : lines) {
              prependBuffer.append(line);
              prependBuffer.append("\n");
              lineOffset++;
            }
          }
        } catch (IOException e) {
          e.printStackTrace();
        }
      }
    }
    compiler.setPrependedLineOffset(lineOffset);
    String moduleCode = compiler.toSource(module);

    boolean hasGlobalScopeName =
        !Strings.isNullOrEmpty(config.getGlobalScopeName())
            && config.getCompilationMode() != CompilationMode.WHITESPACE;

    // Optionally wrap the module in an anonymous function, with the
    // requisite wrapper to make it work.
    if (hasGlobalScopeName) {
      if (isRootModule) {
        builder.append(prependBuffer.toString());
        // Initialize the global scope in the root module.
        builder.append(config.getGlobalScopeName());
        builder.append("={};");
      }
      builder.append("(function(");
      builder.append(Config.GLOBAL_SCOPE_NAME);
      // Including a newline makes the offset into the source map easier to calculate.
      builder.append("){\n");
    }
    builder.append(moduleCode);
    if (hasGlobalScopeName) {
      builder.append("})(");
      builder.append(config.getGlobalScopeName());
      builder.append(");");
    }

    if (resetSourceMap) {
      SourceMap sourceMap = compiler.getSourceMap();
      if (sourceMap != null) sourceMap.reset();
    }

    // http://code.google.com/p/closure-library/issues/detail?id=196
    // http://blog.getfirebug.com/2009/08/11/give-your-eval-a-name-with-sourceurl/
    // non-root modules are loaded with eval, give it a sourceURL for better
    // debugging
    if (!isRootModule) {
      builder.append("\n//@ sourceURL=" + moduleNameToUri.apply(moduleName));
    }

    return builder.toString();
  }