diff --git a/src/com/google/javascript/jscomp/deps/ClosureBundler.java b/src/com/google/javascript/jscomp/deps/ClosureBundler.java index 949d570eb5c..6d80a17c8cf 100644 --- a/src/com/google/javascript/jscomp/deps/ClosureBundler.java +++ b/src/com/google/javascript/jscomp/deps/ClosureBundler.java @@ -15,27 +15,47 @@ */ package com.google.javascript.jscomp.deps; +import com.google.common.base.Strings; import com.google.common.io.CharSource; import com.google.common.io.Files; - +import com.google.javascript.jscomp.transpile.TranspileResult; +import com.google.javascript.jscomp.transpile.Transpiler; import java.io.File; import java.io.IOException; import java.nio.charset.Charset; - +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; /** * A utility class to assist in creating JS bundle files. */ public class ClosureBundler { - private boolean useEval = false; + + private final Transpiler transpiler; + + private EvalMode mode = EvalMode.NORMAL; private String sourceUrl = null; private String path = "unknown_source"; + // TODO(sdh): This cache should be moved out into a higher level, but is + // currently required due to the API that source maps must be accessible + // via just a path (and not the file contents). + private final Map sourceMapCache = new ConcurrentHashMap<>(); + + // TODO(sdh): This causes serious problems with edit-refresh if the bundler + // is used in the wrong scope. The logic must be moved outside this class. + private boolean shouldSendRuntime = true; + public ClosureBundler() { + this(Transpiler.NULL); + } + + public ClosureBundler(Transpiler transpiler) { + this.transpiler = transpiler; } public final ClosureBundler useEval(boolean useEval) { - this.useEval = useEval; + this.mode = useEval ? EvalMode.EVAL : EvalMode.NORMAL; return this; } @@ -78,10 +98,19 @@ public void appendTo( Appendable out, DependencyInfo info, CharSource content) throws IOException { + // TODO(sdh): Move this logic into the bundle manager. + if (shouldSendRuntime) { + String runtime = transpiler.runtime(); + if (!runtime.isEmpty()) { + mode.appendTraditional(runtime, out, null); + } + shouldSendRuntime = false; + } + if (info.isModule()) { - appendGoogModule(out, content); + mode.appendGoogModule(transpile(content.read()), out, sourceUrl); } else { - appendTraditional(out, content); + mode.appendTraditional(transpile(content.read()), out, sourceUrl); } } @@ -90,44 +119,60 @@ public void appendTo( * method. */ public String getSourceMap(String path) { - return ""; + return Strings.nullToEmpty(sourceMapCache.get(path)); } - private void appendTraditional(Appendable out, CharSource contents) - throws IOException { - if (useEval) { - out.append("(0,eval(\""); - append(out, Mode.ESCAPED, contents); - appendSourceUrl(out, Mode.ESCAPED); - out.append("\"));\n"); - } else { - append(out, Mode.NORMAL, contents); - appendSourceUrl(out, Mode.NORMAL); - } + private String transpile(String s) { + TranspileResult result = transpiler.transpile(path, s); + sourceMapCache.put(path, result.sourceMap()); + return result.transpiled(); } - private void appendGoogModule(Appendable out, CharSource contents) - throws IOException { - if (useEval) { - out.append("goog.loadModule(\""); - append(out, Mode.ESCAPED, contents); - appendSourceUrl(out, Mode.ESCAPED); - out.append("\");\n"); - } else { - // add the prefix on the first line so the line numbers aren't affected. - out.append( - "goog.loadModule(function(exports) {" - + "'use strict';"); - append(out, Mode.NORMAL, contents); - out.append( - "\n" // terminate any trailing single line comment. - + ";" // terminate any trailing expression. - + "return exports;});\n"); - appendSourceUrl(out, Mode.NORMAL); - } + private enum EvalMode { + EVAL { + @Override + void appendTraditional(String s, Appendable out, String sourceUrl) throws IOException { + out.append("(0,eval(\""); + EscapeMode.ESCAPED.append(s, out); + appendSourceUrl(out, EscapeMode.ESCAPED, sourceUrl); + out.append("\"));\n"); + } + + @Override + void appendGoogModule(String s, Appendable out, String sourceUrl) throws IOException { + out.append("goog.loadModule(\""); + EscapeMode.ESCAPED.append(s, out); + appendSourceUrl(out, EscapeMode.ESCAPED, sourceUrl); + out.append("\");\n"); + } + }, + NORMAL { + @Override + void appendTraditional(String s, Appendable out, String sourceUrl) throws IOException { + EscapeMode.NORMAL.append(s, out); + appendSourceUrl(out, EscapeMode.NORMAL, sourceUrl); + } + + @Override + void appendGoogModule(String s, Appendable out, String sourceUrl) throws IOException { + // add the prefix on the first line so the line numbers aren't affected. + out.append( + "goog.loadModule(function(exports) {" + + "'use strict';"); + EscapeMode.NORMAL.append(s, out); + out.append( + "\n" // terminate any trailing single line comment. + + ";" // terminate any trailing expression. + + "return exports;});\n"); + appendSourceUrl(out, EscapeMode.NORMAL, sourceUrl); + } + }; + + abstract void appendTraditional(String s, Appendable out, String sourceUrl) throws IOException; + abstract void appendGoogModule(String s, Appendable out, String sourceUrl) throws IOException; } - private enum Mode { + private enum EscapeMode { ESCAPED { @Override void append(String s, Appendable out) throws IOException { out.append(SourceCodeEscapers.javascriptEscaper().escape(s)); @@ -142,17 +187,8 @@ private enum Mode { abstract void append(String s, Appendable out) throws IOException; } - private void append(Appendable out, Mode mode, String s) throws IOException { - String transformed = transformInput(s, path); - mode.append(transformed, out); - } - - private void append(Appendable out, Mode mode, CharSource cs) + private static void appendSourceUrl(Appendable out, EscapeMode mode, String sourceUrl) throws IOException { - append(out, mode, cs.read()); - } - - private void appendSourceUrl(Appendable out, Mode mode) throws IOException { if (sourceUrl == null) { return; } @@ -161,13 +197,4 @@ private void appendSourceUrl(Appendable out, Mode mode) throws IOException { // but source URLs generally aren't valid JS inputs. mode.append(toAppend, out); } - - /** - * Template method. Subclasses that need to transform the inputs should override this method. - * (For example, {@link TranspilingClosureBundler#transformInput} transpiles inputs from ES6 - * to ES5.) - */ - protected String transformInput(String input, String path) { - return input; - } } diff --git a/src/com/google/javascript/jscomp/deps/TranspilingClosureBundler.java b/src/com/google/javascript/jscomp/deps/TranspilingClosureBundler.java deleted file mode 100644 index 9fdd057e5fa..00000000000 --- a/src/com/google/javascript/jscomp/deps/TranspilingClosureBundler.java +++ /dev/null @@ -1,199 +0,0 @@ -/* - * Copyright 2016 The Closure Compiler Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.javascript.jscomp.deps; - -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Throwables; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.collect.ImmutableList; -import com.google.common.hash.HashFunction; -import com.google.common.hash.Hashing; -import com.google.common.io.CharSource; -import com.google.common.util.concurrent.UncheckedExecutionException; -import com.google.javascript.jscomp.Compiler; -import com.google.javascript.jscomp.CompilerOptions; -import com.google.javascript.jscomp.CompilerOptions.LanguageMode; -import com.google.javascript.jscomp.PropertyRenamingPolicy; -import com.google.javascript.jscomp.Result; -import com.google.javascript.jscomp.SourceFile; -import com.google.javascript.jscomp.VariableRenamingPolicy; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.util.Map; -import java.util.concurrent.Callable; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import javax.annotation.concurrent.NotThreadSafe; - -/** - * {@link ClosureBundler} that transpiles its sources. - */ -@NotThreadSafe -public final class TranspilingClosureBundler extends ClosureBundler { - private static final HashFunction HASH_FUNCTION = Hashing.goodFastHash(64); - private static final int DEFAULT_CACHE_SIZE = 100; - /** - * Cache recent transpilations, keyed by the hash code of the input - * to avoid storing the whole input. - */ - @VisibleForTesting final Cache cachedTranspilations; - - // TODO(sdh): Not all transpilation requires the runtime, only inject if actually needed. - private final String es6Runtime; - private boolean needToBundleEs6Runtime = true; - // Whether to inline source map info directly into the output, in a "// #sourceMappingUrl" - // comment. This bloats the size of the transpiled output, but it allows the server to avoid - // serving the source map separately. - private final boolean inlineSourceMap; - // Map of source paths to generated source map paths. - private final Map sourceMapCache = new ConcurrentHashMap<>(); - - public TranspilingClosureBundler() { - this(getEs6Runtime()); - } - - /** - * Creates a new bundler that transpile the sources from ES6 to ES5. - * - * @param transpilationCache The cache to use to store already transpiled files - */ - public TranspilingClosureBundler( - Cache transpilationCache, boolean inlineSourceMap) { - this(getEs6Runtime(), transpilationCache, inlineSourceMap); - } - - @VisibleForTesting - TranspilingClosureBundler(String es6Runtime) { - this( - es6Runtime, - CacheBuilder.newBuilder().maximumSize(DEFAULT_CACHE_SIZE).build(), - true); - } - - @VisibleForTesting - TranspilingClosureBundler( - String es6Runtime, Cache transpilationCache, boolean inlineSourceMap) { - this.es6Runtime = es6Runtime; - this.cachedTranspilations = transpilationCache; - this.inlineSourceMap = inlineSourceMap; - } - - @Override - public void appendTo(Appendable out, DependencyInfo info, CharSource content) throws IOException { - if (needToBundleEs6Runtime) { - // Piggyback on the first call to transformInput to include the ES6 runtime as well. - super.appendTo(out, SimpleDependencyInfo.EMPTY, CharSource.wrap(es6Runtime)); - needToBundleEs6Runtime = false; - } - super.appendTo(out, info, content); - } - - private static CompilerOptions getOptions() { - CompilerOptions options = new CompilerOptions(); - options.setLanguageIn(LanguageMode.ECMASCRIPT6_STRICT); - options.setLanguageOut(LanguageMode.ECMASCRIPT5); - // Quoting keyword properties is only needed in ES3, so basically only in IE8. - // But we set it explicitly here because the way the test bundler works, it invokes - // the compiler without giving information about the browser, so we have to quote - // every time to be safe :-/ - options.setQuoteKeywordProperties(true); - options.setSkipNonTranspilationPasses(true); - options.setVariableRenaming(VariableRenamingPolicy.OFF); - options.setPropertyRenaming(PropertyRenamingPolicy.OFF); - options.setWrapGoogModulesForWhitespaceOnly(false); - options.setPrettyPrint(true); - options.setSourceMapOutputPath("/dev/null"); - options.setSourceMapIncludeSourcesContent(true); - return options; - } - - @Override - protected String transformInput(final String js, final String path) { - try { - // Don't use built-in hashCode to decrease the likelihood of a collision. - long hashCode = HASH_FUNCTION.hashString(js, StandardCharsets.UTF_8).asLong(); - return cachedTranspilations.get( - hashCode, - new Callable() { - @Override - public String call() throws IOException { - // Neither the compiler nor the options is thread safe, so they can't be - // saved as instance state. - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - Compiler compiler = new Compiler(new PrintStream(baos)); - SourceFile sourceFile = SourceFile.fromCode(path, js); - Result result = - compiler.compile( - ImmutableList.of(), ImmutableList.of(sourceFile), getOptions()); - if (compiler.getErrorManager().getErrorCount() > 0) { - String message; - try { - message = baos.toString(StandardCharsets.UTF_8.name()); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); - } - throw new IllegalStateException(message); - } - if (!result.transpiledFiles.contains(sourceFile)) { - return js; - } - StringBuilder source = new StringBuilder().append(compiler.toSource()); - StringBuilder sourceMap = new StringBuilder(); - compiler.getSourceMap().appendTo(sourceMap, path); - sourceMapCache.put(path, sourceMap.toString()); - if (inlineSourceMap) { - source - .append("\n//# sourceMappingURL=data:,") - .append(URLEncoder.encode(sourceMap.toString(), "UTF-8").replace("+", "%20")); - } - return source.append("\n").toString(); - } - }); - } catch (ExecutionException | UncheckedExecutionException e) { - // IllegalStateExceptions thrown from the callable above will end up here as - // UncheckedExecutionExceptions, per the contract of Cache#get. Throw the underlying - // IllegalStateException so that the compiler error message is at the top of the stack trace. - if (e.getCause() instanceof IllegalStateException) { - throw (IllegalStateException) e.getCause(); - } else { - throw Throwables.propagate(e); - } - } - } - - @Override - public String getSourceMap(final String path) { - return sourceMapCache.get(path); - } - - /** Generates the runtime by requesting the "es6_runtime" library from the compiler. */ - private static String getEs6Runtime() { - CompilerOptions options = getOptions(); - options.setLanguageOut(LanguageMode.ECMASCRIPT3); // change .delete to ['delete'] - options.setForceLibraryInjection(ImmutableList.of("es6_runtime")); - Compiler compiler = new Compiler(); - SourceFile sourceFile = SourceFile.fromCode("source", ""); - compiler.compile( - ImmutableList.of(), ImmutableList.of(sourceFile), options); - return compiler.toSource(); - } -} diff --git a/src/com/google/javascript/jscomp/transpile/CachingTranspiler.java b/src/com/google/javascript/jscomp/transpile/CachingTranspiler.java index 68cab45d3b9..65cd8c77350 100644 --- a/src/com/google/javascript/jscomp/transpile/CachingTranspiler.java +++ b/src/com/google/javascript/jscomp/transpile/CachingTranspiler.java @@ -35,12 +35,6 @@ public final class CachingTranspiler implements Transpiler { private final LoadingCache cache; private final Supplier runtime; - private static final String DEFAULT_SPEC = "maximumSize=100"; - - public CachingTranspiler(Transpiler delegate) { - this(delegate, CacheBuilder.from(DEFAULT_SPEC)); - } - public CachingTranspiler( final Transpiler delegate, CacheBuilder builder) { checkNotNull(delegate); diff --git a/src/com/google/javascript/jscomp/transpile/TranspilerBuilder.java b/src/com/google/javascript/jscomp/transpile/TranspilerBuilder.java new file mode 100644 index 00000000000..3c85534fbbf --- /dev/null +++ b/src/com/google/javascript/jscomp/transpile/TranspilerBuilder.java @@ -0,0 +1,79 @@ +/* + * Copyright 2016 The Closure Compiler Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.javascript.jscomp.transpile; + +import com.google.common.cache.CacheBuilder; +import javax.annotation.CheckReturnValue; + +/** + * Basic Transpiler implementation for outputting ES5 code. + */ +public final class TranspilerBuilder { + + /** + * Returns a new TranspilerBuilder that transpiles down to ES5. + */ + public static TranspilerBuilder toEs5() { + return TO_ES5; + } + + private static final TranspilerBuilder TO_ES5 = + new TranspilerBuilder( + new BaseTranspiler(new BaseTranspiler.CompilerSupplier(), "es6_runtime")); + + private final Transpiler transpiler; + + TranspilerBuilder(Transpiler transpiler) { + this.transpiler = transpiler; + } + + /** + * Returns a TranspilerBuilder with cached transpilations, using the default + * cache settings (maximum size of 10,000). Note that the builder itself is + * not changed. + */ + @CheckReturnValue + public TranspilerBuilder caching() { + return caching(DEFAULT_CACHE_SPEC); + } + private static final String DEFAULT_CACHE_SPEC = "maximumSize=10000"; + + /** + * Returns a TranspilerBuilder with cached transpilations, using the given + * cache spec. Note that the builder itself is not changed. + */ + @CheckReturnValue + public TranspilerBuilder caching(String spec) { + return caching(CacheBuilder.from(spec)); + } + + /** + * Returns a TranspilerBuilder with cached transpilations, using the given + * cache builder. Note that the builder itself is not changed. + */ + @CheckReturnValue + public TranspilerBuilder caching(CacheBuilder builder) { + return new TranspilerBuilder(new CachingTranspiler(transpiler, builder)); + } + + /** + * Returns the built Transpiler. + */ + public Transpiler build() { + return transpiler; + } +} diff --git a/test/com/google/javascript/jscomp/deps/ClosureBundlerTest.java b/test/com/google/javascript/jscomp/deps/ClosureBundlerTest.java index 0a3d358c917..35e5f4cb2ad 100644 --- a/test/com/google/javascript/jscomp/deps/ClosureBundlerTest.java +++ b/test/com/google/javascript/jscomp/deps/ClosureBundlerTest.java @@ -16,10 +16,14 @@ package com.google.javascript.jscomp.deps; import static com.google.common.truth.Truth.assertThat; +import static org.mockito.Mockito.RETURNS_SMART_NULLS; +import static org.mockito.Mockito.when; -import junit.framework.TestCase; - +import com.google.javascript.jscomp.transpile.TranspileResult; +import com.google.javascript.jscomp.transpile.Transpiler; import java.io.IOException; +import junit.framework.TestCase; +import org.mockito.Mockito; /** * Tests for ClosureBundler @@ -102,4 +106,27 @@ public void testTraditionalWithEvalWithSourceUrl() throws IOException { assertThat(sb.toString()) .isEqualTo("(0,eval(\"\\x22a string\\x22\\n//# sourceURL\\x3dURL\\n\"));\n"); } + + public void testTranspilation() throws IOException { + String input = "goog.module('Foo');\nclass Foo {}"; + + Transpiler transpiler = Mockito.mock(Transpiler.class, RETURNS_SMART_NULLS); + when(transpiler.runtime()).thenReturn("RUNTIME;"); + when(transpiler.transpile("foo.js", input)) + .thenReturn(new TranspileResult("foo.js", input, "TRANSPILED;", "")); + + ClosureBundler bundler = new ClosureBundler(transpiler).withPath("foo.js"); + StringBuilder sb = new StringBuilder(); + bundler.appendTo(sb, MODULE, input); + assertThat(sb.toString()) + .isEqualTo("RUNTIME;goog.loadModule(function(exports) {'use strict';TRANSPILED;\n" + + ";return exports;});\n"); + + // Second call doesn't include runtime anymore. + sb = new StringBuilder(); + bundler.appendTo(sb, MODULE, input); + assertThat(sb.toString()) + .isEqualTo("goog.loadModule(function(exports) {'use strict';TRANSPILED;\n" + + ";return exports;});\n"); + } } diff --git a/test/com/google/javascript/jscomp/deps/TranspilingClosureBundlerTest.java b/test/com/google/javascript/jscomp/deps/TranspilingClosureBundlerTest.java deleted file mode 100644 index 710cc28e7b2..00000000000 --- a/test/com/google/javascript/jscomp/deps/TranspilingClosureBundlerTest.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2016 The Closure Compiler Authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.javascript.jscomp.deps; - -import static com.google.common.truth.Truth.assertThat; - -import com.google.common.cache.CacheBuilder; -import com.google.javascript.jscomp.testing.BlackHoleErrorManager; - -import junit.framework.TestCase; -import org.junit.Assert; - -import java.io.IOException; - -/** Tests for {@link TranspilingClosureBundler}. */ -public final class TranspilingClosureBundlerTest extends TestCase { - - private static final JsFileParser PARSER = new JsFileParser(new BlackHoleErrorManager()); - private TranspilingClosureBundler bundler; - - @Override - public void setUp() { - bundler = new TranspilingClosureBundler("RUNTIME;\n"); - } - - public void testCodeWithES6FeaturesIsTranspiled() throws IOException { - String input = "class Foo {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("RUNTIME;\nvar Foo = function() {\n};\n"); - } - - public void testRuntimeInjectedOutsideBundle() throws IOException { - String input = "goog.module('Foo');\nclass Foo {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("RUNTIME;\n" - + "goog.loadModule(function(exports) {'use strict';" - + "goog.module(\"Foo\");\nvar Foo = function() {\n};\n\n" - + ";return exports;});\n"); - } - - public void testCacheHit() throws IOException { - String input = "class CacheHit {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - assertThat(bundler.cachedTranspilations.asMap()).isEmpty(); - - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("RUNTIME;\nvar CacheHit = function() {\n};\n"); - assertThat(bundler.cachedTranspilations.asMap()).hasSize(2); - - sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - // The ES6 runtime isn't bundled a second time. - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("var CacheHit = function() {\n};\n"); - assertThat(bundler.cachedTranspilations.asMap()).hasSize(2); - } - - public void testCacheMiss() throws IOException { - String input = "class Foo {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("RUNTIME;\nvar Foo = function() {\n};\n"); - assertThat(bundler.cachedTranspilations.asMap()).hasSize(2); - - input = "class Bar {}"; - sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - // The ES6 runtime isn't bundled a second time. - assertThat(removeSourceMap(sb.toString())) - .isEqualTo("var Bar = function() {\n};\n"); - assertThat(bundler.cachedTranspilations.asMap()).hasSize(3); - } - - public void testError() throws IOException { - String input = "const foo;"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - StringBuilder sb = new StringBuilder(); - try { - bundler.appendTo(sb, info, input); - } catch (IllegalStateException e) { - assertThat(e.getMessage()).contains("Parse error. const variables must have an initializer"); - return; - } - Assert.fail(); - } - - public void testDisableSourceMap() throws IOException { - bundler = - new TranspilingClosureBundler( - CacheBuilder.newBuilder().maximumSize(1).build(), false); - String input = "class Foo {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - assertThat(sb.toString()).doesNotContain("//# sourceMappingURL="); - } - - public void testGetSourceMapReturnsAfterTranspile() throws IOException { - bundler = - new TranspilingClosureBundler( - CacheBuilder.newBuilder().maximumSize(1).build(), false); - bundler.withPath("foo.js"); - assertThat(bundler.getSourceMap("foo.js")).isNull(); - String input = "class Foo {}"; - DependencyInfo info = PARSER.parseFile("foo.js", "foo.js", input); - StringBuilder sb = new StringBuilder(); - bundler.appendTo(sb, info, input); - String sourceMap = bundler.getSourceMap("foo.js"); - assertThat(sourceMap).isNotNull(); - String input2 = "class Bar {}"; - info = PARSER.parseFile("foo.js", "foo.js", input2); - bundler.appendTo(sb, info, input2); - assertThat(bundler.getSourceMap("foo.js")).isNotEqualTo(sourceMap); - } - - private static String removeSourceMap(String input) { - return input.replaceAll("\n//# sourceMappingURL[^\n]+\n", ""); - } -} diff --git a/test/com/google/javascript/jscomp/transpile/CachingTranspilerTest.java b/test/com/google/javascript/jscomp/transpile/CachingTranspilerTest.java index 44b87099aa7..552650dea1d 100644 --- a/test/com/google/javascript/jscomp/transpile/CachingTranspilerTest.java +++ b/test/com/google/javascript/jscomp/transpile/CachingTranspilerTest.java @@ -22,6 +22,7 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import com.google.common.cache.CacheBuilder; import junit.framework.TestCase; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -39,7 +40,7 @@ public final class CachingTranspilerTest extends TestCase { @Override public void setUp() { MockitoAnnotations.initMocks(this); - transpiler = new CachingTranspiler(delegate); + transpiler = new CachingTranspiler(delegate, CacheBuilder.newBuilder()); } public void testTranspileDelegates() {