001/* 002 * Copyright 2018 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.minimize.css; 017 018import java.net.URI; 019import java.net.URISyntaxException; 020import java.util.Arrays; 021import java.util.function.Predicate; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025/** 026 * Helper for JSASS source maps 027 */ 028public final class JSASSFixHelper 029{ 030 031 // Regex that matches a JSASS injected import in the sources of a source map 032 private static final Pattern SOURCEMAP_SOURCES_PATTERN = Pattern.compile("^([^\"]*\")" // Captures the start of the line until the first double quote 033 + "([^\"]*)" // Captures any character that is the path of the JSASS injected import 034 + "(\",?)$", // Captures the end of the line, which is the closing double quote, and an optional comma 035 Pattern.MULTILINE); 036 037 // List of JSASS files injected automatically in the source map 038 private static final String[] JSASS_INJECTED_FILES = new String[] {"JSASS_CUSTOM.scss", "JSASS_PRE_IMPORT.scss", "JSASS_POST_IMPORT.scss"}; 039 040 private JSASSFixHelper() 041 { 042 // static helper 043 } 044 045 /** 046 * Fix the common problems with JSASS generated source maps 047 * @param sourceMap The source map 048 * @param externalContextPath The context path 049 * @return The source map fixed 050 */ 051 public static String fixJsassSourceMap(String sourceMap, String externalContextPath) 052 { 053 // Replace relative path with absolute 054 Matcher matcher = SOURCEMAP_SOURCES_PATTERN.matcher(sourceMap); 055 StringBuffer sb = new StringBuffer(); 056 while (matcher.find()) 057 { 058 String source = matcher.group(2); 059 Predicate<String> isJsassInjectedFile = file -> source.endsWith(file); 060 if (Arrays.stream(JSASS_INJECTED_FILES).anyMatch(isJsassInjectedFile)) 061 { 062 // JSASS injected file, replace with placeholder 063 matcher.appendReplacement(sb, "$1" + externalContextPath + "/plugins/core/resources/css/jsass_injected_file_placeholder.css$3"); 064 } 065 else 066 { 067 try 068 { 069 if (!source.startsWith("/") && !(new URI(source).isAbsolute())) 070 { 071 // To prevent JSASS from resolving URIs, URIs are transformed to relative URIs before going through JSASS, 072 // and must be converted back to absolute (by adding the initial / at the start) afterward. 073 matcher.appendReplacement(sb, "$1/$2$3"); 074 } 075 } 076 catch (URISyntaxException e) 077 { 078 // invalid uri in sources of the sourceMap. 079 } 080 } 081 } 082 083 matcher.appendTail(sb); 084 085 return sb.toString(); 086 } 087 088}