From 17690f2d73b640cf4bbf082fe96f5b3075a040fe Mon Sep 17 00:00:00 2001 From: Jo-Philipp Wich Date: Wed, 21 Nov 2018 18:30:14 +0100 Subject: [PATCH] build: add build option to minify *.js files Signed-off-by: Jo-Philipp Wich --- .gitignore | 1 + luci.mk | 14 +- modules/luci-base/Makefile | 3 +- modules/luci-base/src/Makefile | 3 + modules/luci-base/src/jsmin.c | 292 +++++++++++++++++++++++++++++++++ 5 files changed, 311 insertions(+), 2 deletions(-) create mode 100644 modules/luci-base/src/jsmin.c diff --git a/.gitignore b/.gitignore index 07494e98ef..2e4ba9b81a 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ dist/ *.po~ /docs modules/luci-base/src/po2lmo +modules/luci-base/src/jsmin diff --git a/luci.mk b/luci.mk index f9153819ee..aa2e195e27 100644 --- a/luci.mk +++ b/luci.mk @@ -84,7 +84,7 @@ PKG_GITBRANCH?=$(if $(DUMP),x,$(strip $(shell \ PKG_RELEASE?=1 PKG_INSTALL:=$(if $(realpath src/Makefile),1) PKG_BUILD_DEPENDS += lua/host luci-base/host $(LUCI_BUILD_DEPENDS) -PKG_CONFIG_DEPENDS += CONFIG_LUCI_SRCDIET +PKG_CONFIG_DEPENDS += CONFIG_LUCI_SRCDIET CONFIG_LUCI_JSMIN PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME) @@ -113,6 +113,10 @@ ifeq ($(PKG_NAME),luci-base) bool "Minify Lua sources" default n + config LUCI_JSMIN + bool "Minify JavaScript sources" + default y + menu "Translations"$(foreach lang,$(LUCI_LANGUAGES), config LUCI_LANG_$(lang) @@ -158,6 +162,13 @@ define SrcDiet done endef +define JsMin + $(FIND) $(1) -type f -name '*.js' | while read src; do \ + if jsmin < "$$$$src" > "$$$$src.o"; \ + then mv "$$$$src.o" "$$$$src"; fi; \ + done +endef + define SubstituteVersion $(FIND) $(1) -type f -name '*.htm' | while read src; do \ $(SED) 's/<%# *\([^ ]*\)PKG_VERSION *%>/\1$(PKG_VERSION)/g' \ @@ -177,6 +188,7 @@ define Package/$(PKG_NAME)/install if [ -d $(PKG_BUILD_DIR)/htdocs ]; then \ $(INSTALL_DIR) $(1)$(HTDOCS); \ cp -pR $(PKG_BUILD_DIR)/htdocs/* $(1)$(HTDOCS)/; \ + $(if $(CONFIG_LUCI_JSMIN),$(call JsMin,$(1)$(HTDOCS)/),true); \ else true; fi if [ -d $(PKG_BUILD_DIR)/root ]; then \ $(INSTALL_DIR) $(1)/; \ diff --git a/modules/luci-base/Makefile b/modules/luci-base/Makefile index 06ee7985eb..9bc8ec17a1 100644 --- a/modules/luci-base/Makefile +++ b/modules/luci-base/Makefile @@ -36,13 +36,14 @@ define Host/Configure endef define Host/Compile - $(MAKE) -C src/ clean po2lmo + $(MAKE) -C src/ clean po2lmo jsmin endef define Host/Install $(INSTALL_DIR) $(1)/bin $(INSTALL_DIR) $(1)/lib/lua/5.1 $(INSTALL_BIN) src/po2lmo $(1)/bin/po2lmo + $(INSTALL_BIN) src/jsmin $(1)/bin/jsmin $(INSTALL_BIN) $(HOST_BUILD_DIR)/bin/luasrcdiet $(1)/bin/luasrcdiet $(CP) $(HOST_BUILD_DIR)/luasrcdiet $(1)/lib/lua/5.1/ endef diff --git a/modules/luci-base/src/Makefile b/modules/luci-base/src/Makefile index 03e887e1d5..3e6ead1085 100644 --- a/modules/luci-base/src/Makefile +++ b/modules/luci-base/src/Makefile @@ -4,6 +4,9 @@ clean: rm -f po2lmo parser.so version.lua *.o +jsmin: jsmin.o + $(CC) $(LDFLAGS) -o $@ $^ + po2lmo: po2lmo.o template_lmo.o $(CC) $(LDFLAGS) -o $@ $^ diff --git a/modules/luci-base/src/jsmin.c b/modules/luci-base/src/jsmin.c new file mode 100644 index 0000000000..d23718df39 --- /dev/null +++ b/modules/luci-base/src/jsmin.c @@ -0,0 +1,292 @@ +/* jsmin.c + 2011-09-30 + +Copyright (c) 2002 Douglas Crockford (www.crockford.com) + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +The Software shall be used for Good, not Evil. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +#include +#include + +static int theA; +static int theB; +static int theLookahead = EOF; + + +/* isAlphanum -- return true if the character is a letter, digit, underscore, + dollar sign, or non-ASCII character. +*/ + +static int +isAlphanum(int c) +{ + return ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || + (c >= 'A' && c <= 'Z') || c == '_' || c == '$' || c == '\\' || + c > 126); +} + + +/* get -- return the next character from stdin. Watch out for lookahead. If + the character is a control character, translate it to a space or + linefeed. +*/ + +static int +get() +{ + int c = theLookahead; + theLookahead = EOF; + if (c == EOF) { + c = getc(stdin); + } + if (c >= ' ' || c == '\n' || c == EOF) { + return c; + } + if (c == '\r') { + return '\n'; + } + return ' '; +} + + +/* peek -- get the next character without getting it. +*/ + +static int +peek() +{ + theLookahead = get(); + return theLookahead; +} + + +/* next -- get the next character, excluding comments. peek() is used to see + if a '/' is followed by a '/' or '*'. +*/ + +static int +next() +{ + int c = get(); + if (c == '/') { + switch (peek()) { + case '/': + for (;;) { + c = get(); + if (c <= '\n') { + return c; + } + } + case '*': + get(); + for (;;) { + switch (get()) { + case '*': + if (peek() == '/') { + get(); + return ' '; + } + break; + case EOF: + fprintf(stderr, "Error: JSMIN Unterminated comment.\n"); + exit(1); + } + } + default: + return c; + } + } + return c; +} + + +/* action -- do something! What you do is determined by the argument: + 1 Output A. Copy B to A. Get the next B. + 2 Copy B to A. Get the next B. (Delete A). + 3 Get the next B. (Delete B). + action treats a string as a single character. Wow! + action recognizes a regular expression if it is preceded by ( or , or =. +*/ + +static void +action(int d) +{ + switch (d) { + case 1: + putc(theA, stdout); + case 2: + theA = theB; + if (theA == '\'' || theA == '"' || theA == '`') { + for (;;) { + putc(theA, stdout); + theA = get(); + if (theA == theB) { + break; + } + if (theA == '\\') { + putc(theA, stdout); + theA = get(); + } + if (theA == EOF) { + fprintf(stderr, "Error: JSMIN unterminated string literal."); + exit(1); + } + } + } + case 3: + theB = next(); + if (theB == '/' && (theA == '(' || theA == ',' || theA == '=' || + theA == ':' || theA == '[' || theA == '!' || + theA == '&' || theA == '|' || theA == '?' || + theA == '{' || theA == '}' || theA == ';' || + theA == '\n')) { + putc(theA, stdout); + putc(theB, stdout); + for (;;) { + theA = get(); + if (theA == '[') { + for (;;) { + putc(theA, stdout); + theA = get(); + if (theA == ']') { + break; + } + if (theA == '\\') { + putc(theA, stdout); + theA = get(); + } + if (theA == EOF) { + fprintf(stderr, + "Error: JSMIN unterminated set in Regular Expression literal.\n"); + exit(1); + } + } + } else if (theA == '/') { + break; + } else if (theA =='\\') { + putc(theA, stdout); + theA = get(); + } + if (theA == EOF) { + fprintf(stderr, + "Error: JSMIN unterminated Regular Expression literal.\n"); + exit(1); + } + putc(theA, stdout); + } + theB = next(); + } + } +} + + +/* jsmin -- Copy the input to the output, deleting the characters which are + insignificant to JavaScript. Comments will be removed. Tabs will be + replaced with spaces. Carriage returns will be replaced with linefeeds. + Most spaces and linefeeds will be removed. +*/ + +static void +jsmin() +{ + theA = '\n'; + action(3); + while (theA != EOF) { + switch (theA) { + case ' ': + if (isAlphanum(theB)) { + action(1); + } else { + action(2); + } + break; + case '\n': + switch (theB) { + case '{': + case '[': + case '(': + case '+': + case '-': + action(1); + break; + case ' ': + action(3); + break; + default: + if (isAlphanum(theB)) { + action(1); + } else { + action(2); + } + } + break; + default: + switch (theB) { + case ' ': + if (isAlphanum(theA)) { + action(1); + break; + } + action(3); + break; + case '\n': + switch (theA) { + case '}': + case ']': + case ')': + case '+': + case '-': + case '"': + case '\'': + case '`': + action(1); + break; + default: + if (isAlphanum(theA)) { + action(1); + } else { + action(3); + } + } + break; + default: + action(1); + break; + } + } + } +} + + +/* main -- Output any command line arguments as comments + and then minify the input. +*/ +extern int +main(int argc, char* argv[]) +{ + int i; + for (i = 1; i < argc; i += 1) { + fprintf(stdout, "// %s\n", argv[i]); + } + jsmin(); + return 0; +} -- 2.30.2