diff options
Diffstat (limited to 'client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java')
-rw-r--r-- | client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java b/client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java new file mode 100644 index 0000000000..dda6733384 --- /dev/null +++ b/client-compiler/src/main/java/com/vaadin/sass/linker/SassLinker.java @@ -0,0 +1,233 @@ +/* + * Copyright 2000-2014 Vaadin Ltd. + * + * 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.vaadin.sass.linker; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import org.w3c.css.sac.CSSException; + +import com.google.gwt.core.ext.LinkerContext; +import com.google.gwt.core.ext.TreeLogger; +import com.google.gwt.core.ext.UnableToCompleteException; +import com.google.gwt.core.ext.linker.AbstractLinker; +import com.google.gwt.core.ext.linker.ArtifactSet; +import com.google.gwt.core.ext.linker.EmittedArtifact; +import com.google.gwt.core.ext.linker.LinkerOrder; +import com.google.gwt.core.ext.linker.LinkerOrder.Order; +import com.google.gwt.core.ext.linker.Shardable; +import com.vaadin.sass.internal.ScssStylesheet; + +/** + * Pre-linker that checks for the existence of SASS files in public folders, + * compiles them to CSS files with the SassCompiler from Vaadin and adds the CSS + * back into the artifact. + * + */ +@LinkerOrder(Order.PRE) +@Shardable +public class SassLinker extends AbstractLinker { + + @Override + public String getDescription() { + return "Compiling SCSS files in public folders to standard CSS"; + } + + @Override + public ArtifactSet link(TreeLogger logger, LinkerContext context, + ArtifactSet artifacts, boolean onePermutation) + throws UnableToCompleteException { + + if (!onePermutation) { + // The artifact to return + ArtifactSet toReturn = new ArtifactSet(artifacts); + + // The temporary scss files provided from the artefacts + List<FileInfo> scssFiles = new ArrayList<FileInfo>(); + + // The public files are provided as inputstream, but the compiler + // needs real files, as they can contain references to other + // files. They will be stored here, with their relative paths intact + String tempFolderName = new Date().getTime() + File.separator; + File tempFolder = createTempDir(tempFolderName); + + // Can't search here specifically for public resources, as the type + // is different during compilation. This means we have to loop + // through all the artifacts + for (EmittedArtifact resource : artifacts + .find(EmittedArtifact.class)) { + + // Create the temporary files. + String partialPath = resource.getPartialPath(); + if (partialPath.endsWith(".scss")) { + // In my opinion, the SCSS file does not need to be + // output to the web content folder, as they can't + // be used there + toReturn.remove(resource); + + String fileName = partialPath; + File path = tempFolder; + + int separatorIndex = fileName.lastIndexOf(File.separator); + if (-1 != separatorIndex) { + fileName = fileName.substring(separatorIndex + 1); + + String filePath = partialPath.substring(0, + separatorIndex); + path = createTempDir(tempFolderName + filePath); + } + + File tempfile = new File(path, fileName); + try { + boolean fileCreated = tempfile.createNewFile(); + if (fileCreated) { + + // write the received inputstream to the temp file + writeFromInputStream(resource.getContents(logger), + tempfile); + + // Store the file info for the compilation + scssFiles.add(new FileInfo(tempfile, partialPath)); + } else { + logger.log(TreeLogger.WARN, "Duplicate file " + + tempfile.getPath()); + } + } catch (IOException e) { + logger.log(TreeLogger.ERROR, + "Could not write temporary file " + fileName, e); + } + } + } + + // Compile the files and store them in the artifact + logger.log(TreeLogger.INFO, "Processing " + scssFiles.size() + + " Sass file(s)"); + for (FileInfo fileInfo : scssFiles) { + logger.log(TreeLogger.INFO, " " + fileInfo.originalScssPath + + " -> " + fileInfo.getOriginalCssPath()); + + try { + ScssStylesheet scss = ScssStylesheet.get(fileInfo + .getAbsolutePath()); + if (!fileInfo.isMixin()) { + scss.compile(); + InputStream is = new ByteArrayInputStream(scss + .printState().getBytes()); + + toReturn.add(this.emitInputStream(logger, is, + fileInfo.getOriginalCssPath())); + } + + fileInfo.getFile().delete(); + } catch (CSSException e) { + logger.log(TreeLogger.ERROR, "SCSS compilation failed for " + + fileInfo.getOriginalCssPath(), e); + } catch (IOException e) { + logger.log( + TreeLogger.ERROR, + "Could not write CSS file for " + + fileInfo.getOriginalCssPath(), e); + } catch (Exception e) { + logger.log(TreeLogger.ERROR, "SCSS compilation failed for " + + fileInfo.getOriginalCssPath(), e); + } + } + + return toReturn; + } + + return artifacts; + } + + /** + * Writes the contents of an InputStream out to a file. + * + * @param contents + * @param tempfile + * @throws IOException + */ + private void writeFromInputStream(InputStream contents, File tempfile) + throws IOException { + // write the inputStream to a FileOutputStream + OutputStream out = new FileOutputStream(tempfile); + + int read = 0; + byte[] bytes = new byte[1024]; + + while ((read = contents.read(bytes)) != -1) { + out.write(bytes, 0, read); + } + + contents.close(); + out.flush(); + out.close(); + } + + /** + * Create folder in temporary space on disk. + * + * @param partialPath + * @return + */ + private File createTempDir(String partialPath) { + String baseTempPath = System.getProperty("java.io.tmpdir"); + + File tempDir = new File(baseTempPath + File.separator + partialPath); + if (!tempDir.exists()) { + tempDir.mkdirs(); + } + + tempDir.deleteOnExit(); + return tempDir; + } + + /** + * Temporal storage for file info from Artifact. + */ + private class FileInfo { + private String originalScssPath; + private File file; + + public FileInfo(File file, String originalScssPath) { + this.file = file; + this.originalScssPath = originalScssPath; + } + + public boolean isMixin() { + return file.getName().startsWith("_"); + } + + public String getAbsolutePath() { + return file.getAbsolutePath(); + } + + public String getOriginalCssPath() { + return originalScssPath.substring(0, originalScssPath.length() - 5) + + ".css"; + } + + public File getFile() { + return file; + } + } +} |