File: /var/www/ojs/lib/pkp/tools/buildjs.sh
#!/bin/bash
# @file tools/buildjs.sh
#
# Copyright (c) 2014-2021 Simon Fraser University
# Copyright (c) 2010-2021 John Willinsky
# Distributed under the GNU GPL v3. For full terms see the file docs/COPYING.
#
# Script to check and minimize JavaScript for distribution.
#
# Requirements:
# - Requires Python/Closure Linter and Java/Closure Compiler, see
# <http://code.google.com/closure>. Install this using npm.
# Please see the Closure Linter documentation for installation instructions
# of that tool.
#
# - Requires jslint4java, see <http://code.google.com/p/jslint4java/>. Expects the
# jslint4java.jar (must be renamed!) in the same path as the Closure compiler,
# i.e. in TOOL_PATH as configured below.
#
# - This tool expects to be run from the application's main directory.
#
# Usage: lib/pkp/tools/buildjs.sh [-n]
# ...where -n can be optionally specified to prevent caching.
#
### OS specific configuration ###
# Define a tab to be used inside of sed commands (sed on OSX does not recognize \t)
TAB=$'\t'
# Determine what flag to use for extended regular expressions
if [ `uname` == 'Darwin' ]; then
EXTENDED_REGEX_FLAG='E'
else
EXTENDED_REGEX_FLAG='r'
fi
### Configuration ###
TOOL_PATH=~/bin
CLOSURE_COMPILER_JAR=./node_modules/google-closure-compiler-java/compiler.jar
JS_OUTPUT='js/pkp.min.js'
CLOSURE_EXTERNS='
--externs lib/pkp/tools/closure-externs.js
--externs lib/pkp/tools/closure-externs-check-only.js
--externs lib/pkp/tools/jquery-externs.js'
### Command Line Options ###
OPTIND=1
DO_CACHE=1
while getopts "n" opt; do
case "$opt" in
n) DO_CACHE=0 # No caching
;;
esac
done
shift $((OPTIND-1))
### Start Processing ###
echo >&2
echo "Starting PKP JavaScript builder." >&2
echo "Copyright (c) 2014-2021 Simon Fraser University" >&2
echo "Copyright (c) 2010-2021 John Willinsky" >&2
### Checking Requirements ###
MISSING_REQUIREMENT=''
if [ -z `which gjslint` ]; then
echo >&2
echo "Google Closure Linter not found in PATH. Please go" >&2
echo "to <https://developers.google.com/closure/utilities/docs/linter_howto>" >&2
echo "and make sure that you correctly install the tool before you run" >&2
echo "buildjs.sh." >&2
MISSING_REQUIREMENT='gjslint'
fi
if [ ! -e "$TOOL_PATH/jslint4java.jar" ]; then
echo >&2
echo "JSLint4Java must be installed in the '$TOOL_PATH'" >&2
echo "directory. Please download the tool from" >&2
echo "<http://code.google.com/p/jslint4java/>," >&2
echo "rename it to jslint4java.jar and try again." >&2
MISSING_REQUIREMENT='jslint4java'
fi
if [ ! -e "$CLOSURE_COMPILER_JAR" ]; then
echo >&2
echo "Google Closure Compiler not found in '$CLOSURE_COMPILER_JAR'" >&2
echo "Please run 'npm npm install --save google-closure-compiler' and try again." >&2
MISSING_REQUIREMENT='closure'
fi
if [ -n "$MISSING_REQUIREMENT" ]; then
echo >&2
echo "Exiting!" >&2
exit 1
fi
echo >&2
# A list with all files to be compiled and minified. Expects
# a complete list of script files in registry/minifiedScripts.txt.
COMPILE_FILES=$(sed -n '/^[^#]/p' registry/minifiedScripts.txt)
# FIXME: For now we only check classes as the other
# files contain too many errors to be fixed right now.
LINT_FILES=`echo "$COMPILE_FILES" | egrep -v '^lib/pkp/js/(lib|functions)'`
# Create a working directory in the cache
WORKDIR=`mktemp -dt tmp.XXXXXXXXXX` || { echo "The working directory could not be created\!"; exit 1; }
# Show a list of the files we are going to lint.
echo "Lint..." >&2
echo "Lint..." >"$WORKDIR/.compile-warnings.out"
for JS_FILE in $LINT_FILES; do
echo -n "...$JS_FILE" >&2
echo "...$JS_FILE"
# Prepare file for gjslint and compiler check:
# - transforms whitespace to comply with Google style guide
# - wraps @extends type in curly braces to comply with Google style guide.
# - works around http://code.google.com/p/closure-compiler/issues/detail?id=61 by removing the jQuery closure.
mkdir -p `dirname "$WORKDIR/$JS_FILE"`
sed \
-e "s/^${TAB}//" \
-e "s/${TAB}/ /g" \
-e 's/^(function(\$) {//' \
-e 's/^}(jQuery));//' \
-e 's/@extends \(.*\)$/@extends {\1}/' \
"$JS_FILE" > "$WORKDIR/$JS_FILE"
# Only lint file if it has been changed since last compilation.
if [ ! \( -e "$JS_OUTPUT" \) -o \( "$JS_FILE" -nt "$JS_OUTPUT" \) -o \( "$DO_CACHE" -eq 0 \) ]; then
#############################
### Google Closure Linter ###
#############################
# Run gjslint on the file.
gjslint --strict --nosummary --custom_jsdoc_tags=defgroup,ingroup,file,brief "$WORKDIR/$JS_FILE" | grep '^Line' | sed "s/^/${TAB}/"
##################################
### Douglas Crockford's JSLint ###
##################################
# Run JSLint on the file:
# - Allow for loops without "hasOwnProperty()" check because we operate in an environment
# where additions to the Object prototype are not allowed (same as jQuery).
# - Do not alert on whitespace checking which we prefer to be checked by gjslint.
# This is necessary to remove inconsistency between gjslint's and
# jslint's whitespace rules.
# - We allow dangling underscores (_) to mark private properties and let the
# Closure compiler enforce it.
# - We allow the ++ and == syntax
# - We allow "continue"
# - Multiple var statements in one function are allowed to reduce variable span.
# - We allow code without the 'use strict' pragma as we need the callee property
# for our class framework implementation.
java -jar "$TOOL_PATH/jslint4java.jar" --white --forin --nomen --plusplus --continue \
--eqeq --sloppy --browser --predef pkp,jQuery,alert,tinyMCE,confirm,plupload \
--regexp "$JS_FILE" | sed "s/^/${TAB}/"
echo "...processed!" >&2
else
echo "...skipped!" >&2
fi
done >>"$WORKDIR/.compile-warnings.out"
echo >&2
###############################
### Google Closure Compiler ###
###############################
# Transform lint file list into Closure input parameter list.
LINT_FILES=`echo "$LINT_FILES" | sed "s%^%$WORKDIR/%" | tr '\n' ' ' | sed -$EXTENDED_REGEX_FLAG 's/ $//;s/(^| )/ --js /g'`
# Run Closure - first pass to check with transformed files.
echo >> "$WORKDIR/.compile-warnings.out"
echo "Compile (Check)..." >> "$WORKDIR/.compile-warnings.out"
echo "Compile (Check)..." >&2
java -jar ${CLOSURE_COMPILER_JAR} --language_in=ECMASCRIPT5 --jscomp_warning visibility --warning_level DEFAULT \
$CLOSURE_EXTERNS $LINT_FILES --js_output_file /dev/null 2>&1 \
| sed "s/^/${TAB}/" >>"$WORKDIR/.compile-warnings.out"
# Only minify when there were no warnings.
if [ -n "`cat $WORKDIR/.compile-warnings.out | grep '^ ' | grep -v 'Picked up _JAVA_OPTIONS'`" ]; then
# Issue warnings. If interactive, use "less".
case "$-" in
*i*) less "$WORKDIR/.compile-warnings.out" ;;
*) cat "$WORKDIR/.compile-warnings.out" ;;
esac
echo >&2
echo "Found Errors! Not minified."
echo "Exiting!"
# Remove the temporary directory.
rm -r "$WORKDIR"
exit -1
fi
# Show the list of files we are going to compile:
echo >&2
echo "Compile (Minify)..." >&2
echo "$COMPILE_FILES" | sed 's/^/.../' >&2
# Transform file list into Closure input parameter list.
COMPILE_FILES=`echo "$COMPILE_FILES" | tr '\n' ' ' | sed -$EXTENDED_REGEX_FLAG 's/ $//;s/(^| )/ --js /g'`
# Run Closure - second pass to minify
java -jar ${CLOSURE_COMPILER_JAR} --language_in=ECMASCRIPT5 --jscomp_off checkTypes --warning_level DEFAULT $COMPILE_FILES \
$CLOSURE_EXTERNS --js_output_file "$JS_OUTPUT" 2>&1
echo >&2
echo "Please don't forget to set enable_minified=On in your config.inc.php." >&2
echo >&2
echo "Done!" >&2
# Remove the temporary directory.
rm -r "$WORKDIR"
exit 0