diff --git a/src/.editorconfig b/src/.editorconfig new file mode 100644 index 000000000..a127657e3 --- /dev/null +++ b/src/.editorconfig @@ -0,0 +1,13 @@ +# editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = crlf +indent_size = 4 +indent_style = tab +insert_final_newline = true + +[*.json] +indent_size = 2 +indent_style = space \ No newline at end of file diff --git a/src/React.Core/Babel.cs b/src/React.Core/Babel.cs index b42892473..9c816a5ca 100644 --- a/src/React.Core/Babel.cs +++ b/src/React.Core/Babel.cs @@ -350,12 +350,37 @@ string filename ) { var outputPath = GetOutputPath(filename); - var sourceMapPath = GetSourceMapOutputPath(filename); var contents = _fileSystem.ReadAsString(filename); + if (CacheIsValid(contents, outputPath)) + return outputPath; + var result = TransformWithHeader(filename, contents, null); + + var sourceMapPath = GetSourceMapOutputPath(filename); _fileSystem.WriteAsString(outputPath, result.Code); - _fileSystem.WriteAsString(sourceMapPath, result.SourceMap == null ? string.Empty : result.SourceMap.ToJson()); + _fileSystem.WriteAsString(sourceMapPath, result.SourceMap == null ? string.Empty : result.SourceMap.ToJson()); + return outputPath; } + + /// + /// Checks whether an input file (given as inputFileContents) should be transpiled + /// by calculating the hash and comparing it to the hash value stored + /// in the file given by outputPath. If the outputPath file does not + /// exist the input file should always be transpiled. + /// + /// The contents of the input file. + /// The output path of the (possibly previously) generated file. + /// Returns false if the file should be transpiled, true otherwise. + public virtual bool CacheIsValid(string inputFileContents, string outputPath) + { + if (!_fileSystem.FileExists(outputPath)) + return false; + + var hashForInputFile = _fileCacheHash.CalculateHash(inputFileContents); + var existingOutputContents = _fileSystem.ReadAsString(outputPath); + var fileHasNotChanged = _fileCacheHash.ValidateHash(existingOutputContents, hashForInputFile); + return fileHasNotChanged; + } } } diff --git a/src/React.Tests/Core/BabelTransformerTests.cs b/src/React.Tests/Core/BabelTransformerTests.cs index ed25300ce..f281ee677 100644 --- a/src/React.Tests/Core/BabelTransformerTests.cs +++ b/src/React.Tests/Core/BabelTransformerTests.cs @@ -32,6 +32,10 @@ public void SetUp() _fileSystem = new Mock(); _fileSystem.Setup(x => x.MapPath(It.IsAny())).Returns(x => x); + // Per default the output file should not exist, then individual tests + // can choose otherwise. + _fileSystem.Setup(x => x.FileExists(It.IsAny())).Returns(false); + _fileCacheHash = new Mock(); _babel = new Babel( @@ -152,6 +156,29 @@ public void ShouldSaveTransformationResult() StringAssert.EndsWith("React.DOM.div('Hello World')", result); } + [Test] + public void ShouldSkipTransformationIfCacheIsValid() + { + _fileSystem.Setup(x => x.ReadAsString("foo.jsx")).Returns("
Hello World
"); + _fileSystem.Setup(x => x.FileExists(It.IsAny())).Returns(true); + _fileCacheHash.Setup(x => x.ValidateHash(It.IsAny(), It.IsAny())).Returns(true); + _environment.Setup(x => x.ExecuteWithBabel( + "ReactNET_transform_sourcemap", + It.IsAny(), + It.IsAny(), // Babel config + "foo.jsx" // File name + )).Returns(new JavaScriptWithSourceMap { Code = "React.DOM.div('Hello World')" }); + + string result = null; + _fileSystem.Setup(x => x.WriteAsString("foo.generated.js", It.IsAny())).Callback( + (string filename, string contents) => result = contents + ); + + var resultFilename = _babel.TransformAndSaveFile("foo.jsx"); + Assert.AreEqual("foo.generated.js", resultFilename); + Assert.IsNull(result, "There should be no result. Cached result should have been used."); + } + private void SetUpEmptyCache() { _cache.Setup(x => x.Get("JSX_v3_foo.jsx", null)).Returns((JavaScriptWithSourceMap)null);