JavaScriptModuleBuilder implements IModuleBuilder, IExtensionInitializer, ILayerListener, IShutdownListener, IConfigListener { @Override public ModuleBuild build( String mid, IResource resource, HttpServletRequest request, List<ICacheKeyGenerator> keyGens ) throws Exception { final IAggregator aggr = (IAggregator)request.getAttribute(IAggregator.AGGREGATOR_REQATTRNAME); CompilationLevel level = getCompilationLevel(request); boolean createNewKeyGen = (keyGens == null); boolean isHasFiltering = RequestUtil.isHasFiltering(request); boolean isSourceMaps = aggr.getOptions().isSourceMapsEnabled(); if (!resource.exists()) { if (log.isLoggable(Level.WARNING)) { log.warning( MessageFormat.format( Messages.JavaScriptModuleBuilder_0, new Object[]{resource.getURI().toString()} ) ); } throw new NotFoundException(resource.getURI().toString()); } List<JSSourceFile> sources = this.getJSSource(mid, resource, request, keyGens); JSSource source = null; if (level == null || isSourceMaps) { StringBuffer code = new StringBuffer(); for (JSSourceFile sf : sources) { code.append(sf.getCode()); } source = new JSSource(code.toString(), mid); } boolean coerceUndefinedToFalse = aggr.getConfig().isCoerceUndefinedToFalse(); Features features = (Features)request.getAttribute(IHttpTransport.FEATUREMAP_REQATTRNAME); if (features == null || level == null || !RequestUtil.isHasFiltering(request)) { features = Features.emptyFeatures; coerceUndefinedToFalse = false; } Set<String> discoveredHasConditionals = new LinkedHashSet<String>(); Set<String> hasFiltDiscoveredHasConditionals = new HashSet<String>(); String output = null; Compiler compiler = new Compiler() { @Override public void report(JSError error) { if ("JSC_PARSE_ERROR".equals(error.getType().key)) { if (error.description != null && error.description.contains("Non-JSDoc")) { return; } } super.report(error); } }; CompilerOptions compiler_options = CompilerUtil.getDefaultOptions(); CompilerUtil.applyCompilerOptionsFromMap(compiler_options, compilerOptionsMap); compiler_options.customPasses = HashMultimap.create(); if (isHasFiltering && (level != null || keyGens == null)) { HasFilteringCompilerPass hfcp = new HasFilteringCompilerPass( features, keyGens == null ? hasFiltDiscoveredHasConditionals : null, coerceUndefinedToFalse ); compiler_options.customPasses.put(CustomPassExecutionTime.BEFORE_CHECKS, hfcp); } if (isSourceMaps) { compiler_options.setSourceMapDetailLevel(com.google.javascript.jscomp.SourceMap.DetailLevel.ALL); compiler_options.setSourceMapFormat(com.google.javascript.jscomp.SourceMap.Format.V3); compiler_options.setSourceMapOutputPath(mid); } boolean isReqExpLogging = RequestUtil.isDependencyExpansionLogging(request); boolean isExpandRequires = RequestUtil.isExplodeRequires(request); List<ModuleDeps> expandedDepsList = null; MutableBoolean hasExpandableRequires = new MutableBoolean(false); if (isExpandRequires || createNewKeyGen) { expandedDepsList = new ArrayList<ModuleDeps>(); RequireExpansionCompilerPass recp = new RequireExpansionCompilerPass( aggr, features, discoveredHasConditionals, expandedDepsList, hasExpandableRequires, isExpandRequires, (String)request.getAttribute(IHttpTransport.CONFIGVARNAME_REQATTRNAME), isReqExpLogging, source); compiler_options.customPasses.put(CustomPassExecutionTime.BEFORE_CHECKS, recp); IRequestedModuleNames reqNames = (IRequestedModuleNames)request.getAttribute(IHttpTransport.REQUESTEDMODULENAMES_REQATTRNAME); if (reqNames != null) { reqNames.getExcludes(); } } if (RequestUtil.isExportModuleName(request)) { compiler_options.customPasses.put( CustomPassExecutionTime.BEFORE_CHECKS, new ExportModuleNameCompilerPass(source) ); } if (level != null && level != CompilationLevel.WHITESPACE_ONLY) { level.setOptionsForCompilationLevel(compiler_options); } else { compiler_options.setWarningLevel(DiagnosticGroups.NON_STANDARD_JSDOC, CheckLevel.OFF); } compiler.disableThreads(); String sourceMap = null; Result result = compiler.compile(externs, sources, compiler_options); if (result.success) { if (aggr.getOptions().isDevelopmentMode() && aggr.getOptions().isVerifyDeps()) { List<String> dependentFeatures = aggr.getDependencies().getDependentFeatures(mid); if (dependentFeatures != null) { if (!new HashSet<String>(dependentFeatures).containsAll(hasFiltDiscoveredHasConditionals)) { throw new DependencyVerificationException(mid); } } } discoveredHasConditionals.addAll(hasFiltDiscoveredHasConditionals); if (keyGens != null) { for (ICacheKeyGenerator candidate : keyGens) { if (candidate instanceof CacheKeyGenerator) { CacheKeyGenerator keyGen = (CacheKeyGenerator)candidate; if (keyGen.featureKeyGen == null || keyGen.featureKeyGen.getFeatureSet() == null || !keyGen.featureKeyGen.getFeatureSet().containsAll(discoveredHasConditionals)) { discoveredHasConditionals.addAll(keyGen.featureKeyGen.getFeatureSet()); createNewKeyGen = true; } break; } } } if (level == null) { output = source.toString()+ "\r\n"; if (isSourceMaps) { IdentitySourceMapGenerator gen = new IdentitySourceMapGenerator(mid, output, compiler_options); sourceMap = gen.generateSourceMap(); } } else { output = compiler.toSource(); if (isSourceMaps) { StringWriter srcMapWriter = new StringWriter(); compiler.getSourceMap().appendTo(srcMapWriter, mid); sourceMap = srcMapWriter.toString(); } } } else { StringBuffer sb = new StringBuffer( MessageFormat.format( Messages.JavaScriptModuleBuilder_1, new Object[] {resource.getURI().toString()} ) ); for (JSError error : result.errors) { sb.append("\r\n\t").append(error.description) .append(" (").append(error.lineNumber).append(")."); } if (aggr.getOptions().isDevelopmentMode() || aggr.getOptions().isDebugMode()) { String errorMsg = StringUtil.escapeForJavaScript(sb.toString()); StringBuffer code = new StringBuffer(); for (JSSourceFile sf : sources) { code.append(sf.getCode()); } return new ModuleBuild(code.toString(), null, errorMsg); } else { throw new Exception(sb.toString()); } } Object build = expandedDepsList == null || expandedDepsList.size() == 0 ? output : newJavaScriptBuildRenderer(mid, output, expandedDepsList, isReqExpLogging); if (isSourceMaps && sourceMap != null && !(build instanceof IModuleBuilder)) { String name = sources.get(0).getName(); String src = sources.get(0).getCode(); SourceMap smap = new SourceMap(name, src, sourceMap); build = new SourceMappedBuildRenderer(build, smap); } return new ModuleBuild( build, createNewKeyGen ? getCacheKeyGenerators(discoveredHasConditionals, hasExpandableRequires.getValue()) : keyGens, null); }  static CompilationLevel getCompilationLevel(HttpServletRequest request); @Override void initialize(IAggregator aggregator,
			IAggregatorExtension extension, IExtensionRegistrar registrar); @Override String layerBeginEndNotifier(EventType type, HttpServletRequest request, List<IModule> modules, Set<String> dependentFeatures); @Override void shutdown(IAggregator aggregator); @Override void configLoaded(IConfig config, long sequence); @Override ModuleBuild build(
			String mid,
			IResource resource,
			HttpServletRequest request,
			List<ICacheKeyGenerator> keyGens
			); @Override List<ICacheKeyGenerator> getCacheKeyGenerators(IAggregator aggregator); @Override boolean handles(String mid, IResource resource); @Override boolean isScript(HttpServletRequest request); static final String MODULE_EXPANDED_DEPS; }
@Test public void testBuild() throws Exception { TestUtils.createTestFiles(tmpdir); JsModuleTester p1 = new JsModuleTester("p1/p1", new File(tmpdir, "/p1/p1.js").toURI()); requestAttributes.put(IHttpTransport.OPTIMIZATIONLEVEL_REQATTRNAME, OptimizationLevel.SIMPLE); Future<ModuleBuildReader> future = p1.getBuild(mockRequest); Reader reader = future.get(); StringWriter writer = new StringWriter(); CopyUtil.copy(reader, writer); String compiled = writer.toString(); System.out.println(compiled); List<ICacheKeyGenerator> gens = future.get().getCacheKeyGenerators(); String s = KeyGenUtil.toString(gens); System.out.println(s); assertEquals("expn;js:(has:[conditionFalse, conditionTrue])",s); assertTrue(compiled.contains("has(\"conditionTrue\")")); assertTrue(compiled.contains("has(\"conditionFalse\")")); assertTrue(compiled.contains("condition_True")); assertTrue(compiled.contains("condition_False")); JavaScriptModuleBuilder builder = new JavaScriptModuleBuilder(); List<ICacheKeyGenerator> keyGens = builder.getCacheKeyGenerators(mockAggregator); String cacheFileName = p1.getCachedFileName(KeyGenUtil.generateKey(mockRequest, keyGens)); assertNotNull(cacheFileName); File serializedModule = new File(tmpdir, "a.cached"); ObjectOutputStream os; os = new ObjectOutputStream(new FileOutputStream(serializedModule)); os.writeObject(p1); os.close(); ObjectInputStream is = new ObjectInputStream(new FileInputStream(serializedModule)); p1 = (JsModuleTester) is.readObject(); is.close(); reader = p1.get(mockRequest, true).get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); assertEquals(compiled, writer.toString()); new File(mockAggregator.getCacheManager().getCacheDir(), cacheFileName).delete(); try { p1.get(mockRequest, true); fail("Exception not thrown"); } catch (IOException e) {} requestAttributes.put(IHttpTransport.EXPANDREQUIRELISTS_REQATTRNAME, Boolean.TRUE); ConcurrentListBuilder<String[]> expDeps = new ConcurrentListBuilder<String[]>(); requestAttributes.put(JavaScriptModuleBuilder.MODULE_EXPANDED_DEPS, expDeps); Features features = new Features(); features.put("conditionTrue", true); features.put("conditionFalse", false); requestAttributes.put(IHttpTransport.FEATUREMAP_REQATTRNAME, features); future = p1.getBuild(mockRequest); reader = future.get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); compiled = writer.toString(); System.out.println(compiled); gens = future.get().getCacheKeyGenerators(); String cacheFile1 = p1.getCachedFileName(KeyGenUtil.generateKey(mockRequest, gens)); assertTrue(new File(mockAggregator.getCacheManager().getCacheDir(), cacheFile1).exists()); System.out.println(compiled); Matcher m = Pattern.compile("require([\"p2/a\"].concat(" + JavaScriptModuleBuilder.EXPDEPS_VARNAME + "[0][000]),function(", Pattern.LITERAL).matcher(compiled); Assert.assertTrue(compiled, m.find()); Assert.assertEquals( new HashSet<String>(Arrays.asList(new String[]{"p2/b", "p2/c"})), new HashSet<String>(Arrays.asList(expDeps.toList().get(0)))); assertTrue(compiled.contains("condition_True")); assertFalse(compiled.contains("condition_False")); assertFalse(compiled.contains("has(")); features.put("condition_Foo", true); assertEquals(cacheFile1, p1.getCachedFileName(KeyGenUtil.generateKey(mockRequest, gens))); features.remove("conditionFalse"); reader = p1.getBuild(mockRequest).get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); compiled = writer.toString(); System.out.println(compiled); assertTrue(compiled.contains("condition_True")); assertTrue(compiled.contains("condition_False")); assertFalse(compiled.contains("has(\"conditionTrue\")")); assertTrue(compiled.contains("has(\"conditionFalse\")")); Collection<String> cacheKeys = new TreeSet<String>(p1.getKeys()); System.out.println(cacheKeys); assertEquals("[expn:0;js:S:0:0:1;has{}, expn:0;js:S:1:0:1;has{!conditionFalse,conditionTrue}, expn:0;js:S:1:0:1;has{conditionTrue}]", cacheKeys.toString()); mockAggregator.getOptions().setOption(IOptions.SOURCE_MAPS, true); p1.clearCached(mockAggregator.getCacheManager()); reader = p1.getBuild(mockRequest).get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); compiled = writer.toString(); System.out.println(compiled); SourceMap sourceMap = ((ModuleBuildReader)reader).getSourceMap(); Assert.assertEquals("p1/p1", sourceMap.name); Assert.assertEquals(FileUtils.readFileToString(new File(tmpdir, "/p1/p1.js")), sourceMap.source); System.out.println(sourceMap.map); JSONObject sm = new JSONObject(sourceMap.map); Assert.assertEquals(sm.get("version"), 3); Assert.assertEquals(sm.get("file"), "p1/p1"); Assert.assertEquals(sm.get("lineCount"), 1); Assert.assertEquals(Arrays.asList(((JSONArray)sm.get("sources")).toArray()), Arrays.asList("p1/p1")); Assert.assertEquals(Arrays.asList(((JSONArray)sm.get("names")).toArray()), Arrays.asList("define", "require", "alert", "has")); String mappings = (String)sm.getString("mappings"); Assert.assertNotNull(mappings); Assert.assertTrue(mappings.length() > 0); mockAggregator.getOptions().setOption(IOptions.SOURCE_MAPS, false); p1.clearCached(mockAggregator.getCacheManager()); configRef.set(new ConfigImpl(mockAggregator, tmpdir.toURI(), "{coerceUndefinedToFalse:true}")); p1.clearCached(mockAggregator.getCacheManager()); reader = p1.getBuild(mockRequest).get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); compiled = writer.toString(); System.out.println(compiled); Assert.assertNull(((ModuleBuildReader)reader).getSourceMap()); assertFalse(compiled.contains("has(\"conditionFalse\")")); assertFalse(compiled.contains("conditionFalse")); cacheKeys = new TreeSet<String>(p1.getKeys()); System.out.println(cacheKeys); assertEquals("[expn:0;js:S:1:0:1;has{!conditionFalse,conditionTrue}]", cacheKeys.toString()); Thread.sleep(1500L); new File(tmpdir, "p1/p1.js").setLastModified(new Date().getTime()); requestAttributes.remove(IHttpTransport.FEATUREMAP_REQATTRNAME); mockAggregator.getOptions().setOption("developmentMode", "true"); p1.getBuild(mockRequest).get().close(); cacheKeys = p1.getKeys(); System.out.println(cacheKeys); assertEquals("[expn:0;js:S:1:0:1;has{!conditionFalse,!conditionTrue}]", cacheKeys.toString()); TestUtils.createTestFile(new File(tmpdir, "p1"), "err", TestUtils.err); p1 = new JsModuleTester("p1/err", new File(tmpdir, "p1/err.js").toURI()); requestAttributes.put(IHttpTransport.OPTIMIZATIONLEVEL_REQATTRNAME, OptimizationLevel.SIMPLE); mockAggregator.getOptions().setOption("developmentMode", "false"); future = p1.getBuild(mockRequest); boolean exceptionCaught = false; try { reader = future.get(); } catch (ExecutionException e) { exceptionCaught = true; Assert.assertTrue(e.getCause().getMessage().startsWith("Error compiling module:")); } Assert.assertTrue(exceptionCaught); mockAggregator.getOptions().setOption(IOptions.DEVELOPMENT_MODE, true); future = p1.getBuild(mockRequest); reader = future.get(); writer = new StringWriter(); CopyUtil.copy(reader, writer); compiled = writer.toString(); System.out.println(compiled); Assert.assertEquals(compiled, TestUtils.err); Assert.assertTrue(future.get().isError()); Assert.assertNotNull(future.get().getErrorMessage()); }