001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.minifier;
016    
017    import com.google.javascript.jscomp.BasicErrorManager;
018    import com.google.javascript.jscomp.CheckLevel;
019    import com.google.javascript.jscomp.Compiler;
020    import com.google.javascript.jscomp.CompilerOptions;
021    import com.google.javascript.jscomp.CompilerOptions.LanguageMode;
022    import com.google.javascript.jscomp.DiagnosticGroup;
023    import com.google.javascript.jscomp.DiagnosticGroups;
024    import com.google.javascript.jscomp.DiagnosticType;
025    import com.google.javascript.jscomp.JSError;
026    import com.google.javascript.jscomp.MessageFormatter;
027    import com.google.javascript.jscomp.PropertyRenamingPolicy;
028    import com.google.javascript.jscomp.SourceFile;
029    import com.google.javascript.jscomp.VariableRenamingPolicy;
030    
031    import com.liferay.portal.kernel.log.Log;
032    import com.liferay.portal.kernel.log.LogFactoryUtil;
033    import com.liferay.portal.kernel.util.StringPool;
034    
035    /**
036     * @author Carlos Sierra Andr??s
037     */
038    public class GoogleJavaScriptMinifier implements JavaScriptMinifier {
039    
040            @Override
041            public String compress(String resourceName, String content) {
042                    Compiler compiler = new Compiler(new LogErrorManager());
043    
044                    compiler.disableThreads();
045    
046                    SourceFile sourceFile = SourceFile.fromCode(resourceName, content);
047    
048                    CompilerOptions compilerOptions = new CompilerOptions();
049    
050                    compilerOptions.setLanguageIn(LanguageMode.ECMASCRIPT5);
051                    compilerOptions.setWarningLevel(
052                            DiagnosticGroups.NON_STANDARD_JSDOC, CheckLevel.OFF);
053                    compilerOptions.setWarningLevel(
054                            DiagnosticGroup.forType(
055                                    DiagnosticType.error("JSC_MISSING_PROVIDE_ERROR", "")),
056                            CheckLevel.OFF);
057                    compilerOptions.setWarningLevel(
058                            DiagnosticGroup.forType(
059                                    DiagnosticType.error("JSC_NON_GLOBAL_DEFINE_INIT_ERROR", "")),
060                            CheckLevel.OFF);
061    
062                    setCompileOptions(compilerOptions);
063    
064                    compiler.compile(
065                            SourceFile.fromCode("extern", StringPool.BLANK), sourceFile,
066                            compilerOptions);
067    
068                    return compiler.toSource();
069            }
070    
071            protected void setCompileOptions(CompilerOptions compilerOptions) {
072                    compilerOptions.checkGlobalThisLevel = CheckLevel.OFF;
073                    compilerOptions.closurePass = true;
074                    compilerOptions.coalesceVariableNames = true;
075                    compilerOptions.collapseVariableDeclarations = true;
076                    compilerOptions.convertToDottedProperties = true;
077                    compilerOptions.deadAssignmentElimination = true;
078                    compilerOptions.flowSensitiveInlineVariables = true;
079                    compilerOptions.foldConstants = true;
080                    compilerOptions.labelRenaming = true;
081                    compilerOptions.removeDeadCode = true;
082                    compilerOptions.optimizeArgumentsArray = true;
083                    compilerOptions.setAssumeClosuresOnlyCaptureReferences(false);
084                    compilerOptions.setInlineFunctions(CompilerOptions.Reach.LOCAL_ONLY);
085                    compilerOptions.setInlineVariables(CompilerOptions.Reach.LOCAL_ONLY);
086                    compilerOptions.setRenamingPolicy(
087                            VariableRenamingPolicy.LOCAL, PropertyRenamingPolicy.OFF);
088                    compilerOptions.setRemoveUnusedVariables(
089                            CompilerOptions.Reach.LOCAL_ONLY);
090            }
091    
092            private static final Log _log = LogFactoryUtil.getLog(
093                    GoogleJavaScriptMinifier.class);
094    
095            private static class SimpleMessageFormatter implements MessageFormatter {
096    
097                    @Override
098                    public String formatError(JSError jsError) {
099                            return String.format(
100                                    "(%s:%d): %s", jsError.sourceName, jsError.lineNumber,
101                                    jsError.description);
102                    }
103    
104                    @Override
105                    public String formatWarning(JSError jsError) {
106                            return formatError(jsError);
107                    }
108    
109            }
110    
111            private class LogErrorManager extends BasicErrorManager {
112    
113                    @Override
114                    public void println(CheckLevel checkLevel, JSError jsError) {
115                            if (checkLevel == CheckLevel.ERROR) {
116                                    _log.error(jsError.format(checkLevel, _simpleMessageFormatter));
117                            }
118                            else if (checkLevel == CheckLevel.WARNING) {
119                                    if (_log.isWarnEnabled()) {
120                                            _log.warn(
121                                                    jsError.format(checkLevel, _simpleMessageFormatter));
122                                    }
123                            }
124                    }
125    
126                    @Override
127                    protected void printSummary() {
128                            if (getErrorCount() > 0) {
129                                    _log.error(_buildMessage());
130                            }
131                            else if (_log.isWarnEnabled() && (getWarningCount() > 0)) {
132                                    _log.warn(_buildMessage());
133                            }
134                    }
135    
136                    private String _buildMessage() {
137                            return String.format(
138                                    "{0} error(s), {1} warning(s)", getErrorCount(),
139                                    getWarningCount());
140                    }
141    
142                    private final MessageFormatter _simpleMessageFormatter =
143                            new SimpleMessageFormatter();
144    
145            }
146    
147    }