├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── compile_and_test.sh ├── config ├── src └── ngx_http_sass_module.c └── t ├── conf_include-path.t ├── conf_indent.t ├── conf_is-indented-syntax.t ├── conf_linefeed.t ├── conf_output-style.t ├── conf_precision.t ├── conf_source-comments.t ├── conf_source-map-embed.t ├── fixtures ├── _nginx-dynamic.conf ├── _nginx-static.conf ├── conf_include-path.scss ├── conf_is-indented-syntax.sass ├── conf_output-style.scss ├── conf_precision.scss ├── conf_source-comments.scss └── default.scss └── includes └── _test.scss /.gitignore: -------------------------------------------------------------------------------- 1 | t/servroot/ 2 | vendor/ 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: perl 2 | perl: 5.22 3 | sudo: false 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - libluajit-5.1-2 9 | - libluajit-5.1-dev 10 | 11 | env: 12 | global: 13 | - LUAJIT_LIB=/usr/lib/x86_64-linux-gnu/ 14 | - LUAJIT_INC=/usr/include/luajit-2.0/ 15 | matrix: 16 | - VER_LIBSASS=3.4.9 VER_LUA_NGINX=0.10.15 VER_NGINX=1.14.2 17 | - VER_LIBSASS=3.5.5 VER_LUA_NGINX=0.10.15 VER_NGINX=1.16.1 18 | - VER_LIBSASS=3.6.4 VER_LUA_NGINX=0.10.15 VER_NGINX=1.18.0 19 | - DYNAMIC=true VER_LIBSASS=3.6.4 VER_LUA_NGINX=0.10.15 VER_NGINX=1.18.0 20 | 21 | install: cpanm -v --notest Test::Nginx 22 | script: ./compile_and_test.sh 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-* Marc Neudert 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | 8 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 9 | 10 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Syntactically Awesome NGINX Module 2 | 3 | Providing on-the-fly compiling of [Sass](http://sass-lang.com/) files as an NGINX module. 4 | 5 | Stop thinking about "sass watch" shell processes or the integration features of your IDE while still using the power of Sass while developing your websites. 6 | 7 | Supported versions of LibSass: 8 | 9 | - `3.4.0` (`3.4.9` used on travis) 10 | - `3.5.0` (`3.5.5` used on travis) 11 | - `3.6.0` (`3.6.4` used on travis) 12 | 13 | ### Note 14 | 15 | This module is experimental and only been used in development environments. 16 | 17 | You can, and should, expect some weird bugs from serving unparsed files up to completely deactivating your vhost. 18 | 19 | Use with caution! 20 | 21 | ## Compilation 22 | 23 | ### Prerequisites 24 | 25 | To be able to compile this module you need [LibSass](https://github.com/sass/libsass) available. For a list of tested, and therefore more or less supported, combinations of LibSass and NGINX versions please refer to the travis environment in `.travis.yml`. Using different versions can result in unexpected behaviour or won't work at all. 26 | 27 | Also ldconfig has to find the library: 28 | 29 | ```shell 30 | ldconfig -p | grep "libsass" 31 | ``` 32 | 33 | If it does not show up try to rebuild the index first using *ldconfig* as *root* user and rerun the grep command. Sometimes you need to add the path where LibSass is installed manually to the list of paths ldconfig is looking for libraries in. 34 | 35 | This can be done by adding the path to the *$LD\_LIBRARY\_PATH* variable or the file */etc/ld.so.conf*. Either way, please ensure the command above finds your local LibSass installation before proceeding. 36 | 37 | During compilation the header file `sass.h` has to be available. The files included inside this file are also required. The exact list depends on your LibSass version. 38 | 39 | ### NGINX 40 | 41 | Using this module is as easy as recompiling NGINX from source: 42 | 43 | ```shell 44 | cd /path/to/nginx/src 45 | ./configure --add-module=/path/to/sass-nginx-module 46 | make install 47 | ``` 48 | 49 | Or if you want to have debug logs available: 50 | 51 | ```shell 52 | cd /path/to/nginx/src 53 | ./configure --add-module=/path/to/sass-nginx-module --with-debug 54 | make install 55 | ``` 56 | 57 | To be able to run the unit tests you need additional modules configured: 58 | 59 | ```shell 60 | cd /path/to/nginx/src 61 | 62 | # configure as static module 63 | ./configure \ 64 | --add-module=/projects/public/lua-nginx-module \ 65 | --add-module=/projects/private/sass-nginx-module 66 | 67 | # configure as dynamic module 68 | ./configure \ 69 | --add-module=/projects/public/lua-nginx-module \ 70 | --add-dynamic-module=/projects/private/sass-nginx-module 71 | 72 | make install 73 | ``` 74 | 75 | ## Configuration 76 | 77 | __Note__: If you have compiled a dynamic module you need to also add the `load_module` directive with the appropriate directory. 78 | 79 | The configuration is pretty straightforward: 80 | 81 | ```nginx 82 | server { 83 | location ~ ^.*\.css$ { 84 | sass_compile on; 85 | 86 | rewrite ^(.*)\.css$ $1.scss break; 87 | } 88 | } 89 | ``` 90 | 91 | Add this location block to your server activates the sass compilation. 92 | 93 | Using a rewrite ensures all the magic happens under the hood and you do not have to change your application to load different files. 94 | 95 | ### Parameters 96 | 97 | Error Log Level: 98 | 99 | ```nginx 100 | location / { 101 | # same as regular NGINX error log 102 | sass_error_log error; #default 103 | } 104 | ``` 105 | 106 | Include Path: 107 | 108 | ```nginx 109 | location / { 110 | # windows (semicolon as path sep) 111 | sass_include_path "/some/dir;/another/dir"; 112 | 113 | # everywhere else (colon as path sep) 114 | sass_include_path "/some/dir:/another/dir"; 115 | } 116 | ``` 117 | 118 | Indentation: 119 | 120 | ```nginx 121 | location / { 122 | sass_indent " "; # default 123 | sass_indent " "; 124 | } 125 | ``` 126 | 127 | Indented Syntax (SASS): 128 | 129 | ```nginx 130 | location / { 131 | sass_is_indented_syntax off; # default 132 | sass_is_indented_syntax on; 133 | ``` 134 | 135 | Linefeed: 136 | 137 | ```nginx 138 | location / { 139 | sass_linefeed "\n"; # default 140 | sass_linefeed "\n/* linefeed */\n"; 141 | } 142 | ``` 143 | 144 | Precision of Fractional Numbers: 145 | 146 | ```nginx 147 | location / { 148 | sass_precision 5; # default 149 | sass_precision 3; 150 | } 151 | ``` 152 | 153 | Output Style: 154 | 155 | ```nginx 156 | location / { 157 | sass_output_style compact; 158 | sass_output_style compressed; 159 | sass_output_style expanded; 160 | sass_output_style nested; # default 161 | } 162 | ``` 163 | 164 | Source Comments: 165 | 166 | ```nginx 167 | location / { 168 | sass_source_comments off; # default 169 | sass_source_comments on; 170 | } 171 | ``` 172 | 173 | Source Map (embedded): 174 | 175 | ```nginx 176 | location / { 177 | sass_source_map_embed off; # default 178 | sass_source_map_embed on; 179 | } 180 | ``` 181 | 182 | 183 | ## Testing 184 | 185 | ### Prerequisites 186 | 187 | The unit tests use [Test::Nginx](http://github.com/agentzh/test-nginx) and Lua. 188 | 189 | Please ensure your environment meets the following: 190 | 191 | - `prove` (perl) is available 192 | - `libluajit` is installed 193 | 194 | To be able to run them using `prove` (perl). 195 | 196 | ### Testing Script 197 | 198 | If you fulfill the prerequisites you can use the script `./compile_and_test.sh` to download, compile and test in on go: 199 | 200 | ```shell 201 | VER_LIBSASS=3.6.4 \ 202 | VER_LUA_NGINX=0.10.15 \ 203 | VER_NGINX=1.18.0 \ 204 | LUAJIT_LIB=/usr/lib/x86_64-linux-gnu/ \ 205 | LUAJIT_INC=/usr/include/luajit-2.0/ \ 206 | ./compile_and_test.sh 207 | ``` 208 | 209 | The three passed variables `VER_LIBSASS`, `VER_LUA_NGINX` and `VER_NGINX` define the module versions your are using for compilation. If a variable is not passed to the script it will be automatically taken from your environment. An error message will be printed if no value is available. 210 | 211 | Running the compilation and testing using a dynamic module is possible by additionally passing `DYNAMIC=true` to the script. 212 | 213 | All dependencies will automatically be downloaded to the `./vendor` subfolder. 214 | 215 | To skip the compilation (and download) step you can pass the `--nocompile` flag: 216 | 217 | ```shell 218 | ALL_THE_CONFIGURATION_VARIABLES \ 219 | ./compile_and_test.sh --nocompile 220 | ``` 221 | 222 | Please be aware that (for now) all the variables are still required for the script to run. 223 | 224 | If you want to only run a single test from the testing folder you can pass it as a parameter to the script (and therefore on to `prove`): 225 | 226 | ```shell 227 | # single test 228 | ALL_THE_CONFIGURATION_VARIABLES \ 229 | ./compile_and_test.sh t/conf_output-style.t 230 | 231 | # single test and --nocompile 232 | ALL_THE_CONFIGURATION_VARIABLES \ 233 | ./compile_and_test.sh --nocompile t/conf_output-style.t 234 | ``` 235 | 236 | ## License 237 | 238 | Licensed under the [BSD 2 Clause License](https://opensource.org/licenses/BSD-2-Clause). 239 | -------------------------------------------------------------------------------- /compile_and_test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | cd "${0%/*}" 4 | 5 | moduledir=$(pwd) 6 | nocompile=0 7 | 8 | if [ "${1}" == "--nocompile" ]; then 9 | nocompile=1 10 | 11 | shift 12 | fi 13 | 14 | 15 | echo "==> Checking parameters" 16 | 17 | [ -z "${VER_LIBSASS}" ] && echo 'parameter VER_LIBSASS missing' && exit 1 18 | [ -z "${VER_LUA_NGINX}" ] && echo 'parameter VER_LUA_NGINX missing' && exit 1 19 | [ -z "${VER_NGINX}" ] && echo 'parameter VER_NGINX missing' && exit 1 20 | 21 | [ -z "${LUAJIT_INC}" ] && echo 'parameter LUAJIT_INC missing' && exit 1 22 | [ -z "${LUAJIT_LIB}" ] && echo 'parameter LUAJIT_LIB missing' && exit 1 23 | 24 | 25 | if [ 0 -eq ${nocompile} ]; then 26 | echo "==> Downloading sources" 27 | 28 | [ -z $(which wget) ] && echo 'can not find "wget" to download libraries' && exit 2 29 | 30 | mkdir -p "${moduledir}/vendor" 31 | 32 | cd "${moduledir}/vendor" 33 | 34 | if [ ! -d "nginx-${VER_NGINX}" ]; then 35 | wget -q "http://nginx.org/download/nginx-${VER_NGINX}.tar.gz" -O nginx.tar.gz \ 36 | && tar -xf nginx.tar.gz 37 | fi 38 | 39 | if [ ! -d "lua-nginx-module-${VER_LUA_NGINX}" ]; then 40 | VER_LUA_NGINX_DL="${VER_LUA_NGINX}" 41 | 42 | if [ 40 -ne ${#VER_LUA_NGINX} ]; then 43 | VER_LUA_NGINX_DL="v${VER_LUA_NGINX_DL}" 44 | fi 45 | 46 | wget -q "https://github.com/openresty/lua-nginx-module/archive/${VER_LUA_NGINX_DL}.tar.gz" -O lua-nginx-module.tar.gz \ 47 | && tar -xf lua-nginx-module.tar.gz 48 | fi 49 | 50 | if [ ! -d "libsass-${VER_LIBSASS}" ]; then 51 | wget -q "https://github.com/sass/libsass/archive/${VER_LIBSASS}.tar.gz" -O libsass.tar.gz \ 52 | && tar -xf libsass.tar.gz 53 | fi 54 | 55 | 56 | echo "==> Compiling LibSass" 57 | 58 | cd "${moduledir}/vendor/libsass-${VER_LIBSASS}" 59 | make shared 60 | 61 | 62 | echo "==> Building NGINX" 63 | 64 | cd "${moduledir}/vendor/nginx-${VER_NGINX}" 65 | 66 | if [ ! -z "${DYNAMIC}" ]; then 67 | ./configure \ 68 | --add-module="${moduledir}/vendor/lua-nginx-module-${VER_LUA_NGINX}" \ 69 | --add-dynamic-module="${moduledir}" \ 70 | --with-cc-opt="-I ${moduledir}/vendor/libsass-${VER_LIBSASS}/include" \ 71 | --with-ld-opt="-L ${moduledir}/vendor/libsass-${VER_LIBSASS}/lib" 72 | else 73 | ./configure \ 74 | --add-module="${moduledir}/vendor/lua-nginx-module-${VER_LUA_NGINX}" \ 75 | --add-module="${moduledir}" \ 76 | --with-cc-opt="-I ${moduledir}/vendor/libsass-${VER_LIBSASS}/include" \ 77 | --with-ld-opt="-L ${moduledir}/vendor/libsass-${VER_LIBSASS}/lib" 78 | fi 79 | 80 | make || exit $? 81 | 82 | if [ ! -z "${DYNAMIC}" ]; then 83 | cp "./objs/ngx_http_sass_module.so" "${moduledir}/vendor/" 84 | fi 85 | fi 86 | 87 | 88 | export PATH="$PATH:${moduledir}/vendor/nginx-${VER_NGINX}/objs" 89 | export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${moduledir}/vendor/libsass-${VER_LIBSASS}/lib" 90 | 91 | 92 | echo "==> Testing!" 93 | 94 | cd "${moduledir}" && prove $@ 95 | -------------------------------------------------------------------------------- /config: -------------------------------------------------------------------------------- 1 | ngx_addon_name=ngx_http_sass_module 2 | 3 | if test -n "$ngx_module_link"; then 4 | ngx_module_type=HTTP_AUX_FILTER 5 | ngx_module_name=ngx_http_sass_module 6 | 7 | ngx_module_libs="-lsass" 8 | ngx_module_srcs="$ngx_addon_dir/src/ngx_http_sass_module.c" 9 | 10 | . auto/module 11 | else 12 | CORE_LIBS="$CORE_LIBS -lsass" 13 | HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_sass_module" 14 | NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/src/ngx_http_sass_module.c" 15 | fi 16 | -------------------------------------------------------------------------------- /src/ngx_http_sass_module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | 7 | typedef struct { 8 | ngx_flag_t enable; 9 | ngx_uint_t error_log; 10 | ngx_str_t include_path; 11 | ngx_str_t indent; 12 | ngx_flag_t is_indented_syntax; 13 | ngx_str_t linefeed; 14 | ngx_uint_t output_style; 15 | ngx_uint_t precision; 16 | ngx_flag_t source_comments; 17 | ngx_flag_t source_map_embed; 18 | } ngx_http_sass_loc_conf_t; 19 | 20 | 21 | static ngx_int_t ngx_http_sass_init(ngx_conf_t *cf); 22 | static void *ngx_http_sass_create_loc_conf(ngx_conf_t *cf); 23 | static char *ngx_http_sass_merge_loc_conf(ngx_conf_t *cf,void *parent, void *child); 24 | static char *ngx_http_sass_error_log_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 25 | static char *ngx_http_sass_output_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); 26 | 27 | 28 | static ngx_command_t ngx_http_sass_commands[] = { 29 | { ngx_string("sass_compile"), 30 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 31 | ngx_conf_set_flag_slot, 32 | NGX_HTTP_LOC_CONF_OFFSET, 33 | offsetof(ngx_http_sass_loc_conf_t, enable), 34 | NULL }, 35 | 36 | { ngx_string("sass_error_log"), 37 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 38 | ngx_http_sass_error_log_value, 39 | NGX_HTTP_LOC_CONF_OFFSET, 40 | offsetof(ngx_http_sass_loc_conf_t, error_log), 41 | NULL }, 42 | 43 | { ngx_string("sass_include_path"), 44 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 45 | ngx_conf_set_str_slot, 46 | NGX_HTTP_LOC_CONF_OFFSET, 47 | offsetof(ngx_http_sass_loc_conf_t, include_path), 48 | NULL }, 49 | 50 | { ngx_string("sass_indent"), 51 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 52 | ngx_conf_set_str_slot, 53 | NGX_HTTP_LOC_CONF_OFFSET, 54 | offsetof(ngx_http_sass_loc_conf_t, indent), 55 | NULL }, 56 | 57 | { ngx_string("sass_is_indented_syntax"), 58 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 59 | ngx_conf_set_str_slot, 60 | NGX_HTTP_LOC_CONF_OFFSET, 61 | offsetof(ngx_http_sass_loc_conf_t, is_indented_syntax), 62 | NULL }, 63 | 64 | { ngx_string("sass_linefeed"), 65 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 66 | ngx_conf_set_str_slot, 67 | NGX_HTTP_LOC_CONF_OFFSET, 68 | offsetof(ngx_http_sass_loc_conf_t, linefeed), 69 | NULL }, 70 | 71 | { ngx_string("sass_precision"), 72 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 73 | ngx_conf_set_num_slot, 74 | NGX_HTTP_LOC_CONF_OFFSET, 75 | offsetof(ngx_http_sass_loc_conf_t, precision), 76 | NULL }, 77 | 78 | { ngx_string("sass_output_style"), 79 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, 80 | ngx_http_sass_output_value, 81 | NGX_HTTP_LOC_CONF_OFFSET, 82 | offsetof(ngx_http_sass_loc_conf_t, output_style), 83 | NULL }, 84 | 85 | { ngx_string("sass_source_comments"), 86 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 87 | ngx_conf_set_flag_slot, 88 | NGX_HTTP_LOC_CONF_OFFSET, 89 | offsetof(ngx_http_sass_loc_conf_t, source_comments), 90 | NULL }, 91 | 92 | { ngx_string("sass_source_map_embed"), 93 | NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, 94 | ngx_conf_set_flag_slot, 95 | NGX_HTTP_LOC_CONF_OFFSET, 96 | offsetof(ngx_http_sass_loc_conf_t, source_map_embed), 97 | NULL }, 98 | 99 | ngx_null_command 100 | }; 101 | 102 | 103 | static ngx_str_t ngx_http_sass_type = ngx_string("text/css"); 104 | 105 | 106 | static ngx_str_t err_levels[] = { 107 | ngx_null_string, 108 | ngx_string("emerg"), 109 | ngx_string("alert"), 110 | ngx_string("crit"), 111 | ngx_string("error"), 112 | ngx_string("warn"), 113 | ngx_string("notice"), 114 | ngx_string("info"), 115 | ngx_string("debug") 116 | }; 117 | 118 | 119 | static ngx_http_module_t ngx_http_sass_module_ctx = { 120 | NULL, /* preconfiguration */ 121 | ngx_http_sass_init, /* postconfiguration */ 122 | 123 | NULL, /* create main configuration */ 124 | NULL, /* init main configuration */ 125 | 126 | NULL, /* create server configuration */ 127 | NULL, /* merge server configuration */ 128 | 129 | ngx_http_sass_create_loc_conf, /* create location configuration */ 130 | ngx_http_sass_merge_loc_conf /* merge location configuration */ 131 | }; 132 | 133 | 134 | ngx_module_t ngx_http_sass_module = { 135 | NGX_MODULE_V1, 136 | &ngx_http_sass_module_ctx, /* module context */ 137 | ngx_http_sass_commands, /* module directives */ 138 | NGX_HTTP_MODULE, /* module type */ 139 | 140 | NULL, /* init master */ 141 | NULL, /* init module */ 142 | NULL, /* init process */ 143 | NULL, /* init thread */ 144 | 145 | NULL, /* exit thread */ 146 | NULL, /* exit process */ 147 | NULL, /* exit master */ 148 | NGX_MODULE_V1_PADDING 149 | }; 150 | 151 | 152 | static ngx_int_t 153 | ngx_http_sass_handler(ngx_http_request_t *r) 154 | { 155 | size_t root; 156 | u_char *last; 157 | ngx_buf_t* b; 158 | ngx_chain_t out; 159 | ngx_str_t content, path; 160 | 161 | ngx_http_sass_loc_conf_t *clcf; 162 | 163 | const char *output; 164 | struct Sass_Context *ctx; 165 | struct Sass_File_Context *ctx_file; 166 | struct Sass_Options *options; 167 | 168 | if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { 169 | return NGX_DECLINED; 170 | } 171 | 172 | clcf = ngx_http_get_module_loc_conf(r, ngx_http_sass_module); 173 | 174 | if (!clcf->enable) { 175 | return NGX_DECLINED; 176 | } 177 | 178 | last = ngx_http_map_uri_to_path(r, &path, &root, 0); 179 | 180 | if (NULL == last) { 181 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 182 | } 183 | 184 | path.len = last - path.data; 185 | 186 | ngx_log_debug(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "sass compile file: \"%V\"", &path); 187 | 188 | ctx_file = sass_make_file_context((char *) path.data); 189 | ctx = sass_file_context_get_context(ctx_file); 190 | options = sass_file_context_get_options(ctx_file); 191 | 192 | sass_option_set_include_path(options, (char *) clcf->include_path.data); 193 | sass_option_set_indent(options, (char *) clcf->indent.data); 194 | sass_option_set_input_path(options, (char *) path.data); 195 | sass_option_set_is_indented_syntax_src(options, clcf->is_indented_syntax); 196 | sass_option_set_linefeed(options, (char *) clcf->linefeed.data); 197 | sass_option_set_output_style(options, clcf->output_style); 198 | sass_option_set_precision(options, (int) clcf->precision); 199 | sass_option_set_source_comments(options, clcf->source_comments); 200 | 201 | if (clcf->source_map_embed) { 202 | sass_option_set_source_map_embed(options, true); 203 | sass_option_set_source_map_file(options, "needed-for-feature-activation"); 204 | } 205 | 206 | sass_compile_file_context(ctx_file); 207 | 208 | if (sass_context_get_error_status(ctx) 209 | && sass_context_get_error_message(ctx) 210 | ) { 211 | ngx_log_error(clcf->error_log, r->connection->log, 0, 212 | "sass compilation error: %s", 213 | sass_context_get_error_message(ctx)); 214 | sass_delete_file_context(ctx_file); 215 | 216 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 217 | } else if (sass_context_get_error_status(ctx)) { 218 | ngx_log_error(clcf->error_log, r->connection->log, 0, 219 | "sass compilation error"); 220 | sass_delete_file_context(ctx_file); 221 | 222 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 223 | } 224 | 225 | b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t)); 226 | 227 | if (NULL == b) { 228 | ngx_log_error(clcf->error_log, r->connection->log, 0, 229 | "sass compilation error: %s", 230 | sass_context_get_error_message(ctx)); 231 | sass_delete_file_context(ctx_file); 232 | 233 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 234 | } 235 | 236 | output = sass_context_get_output_string(ctx); 237 | 238 | out.buf = b; 239 | out.next = NULL; 240 | 241 | content.len = sizeof(output) - 1; 242 | content.data = ngx_pnalloc(r->pool, strlen(output)); 243 | 244 | if (NULL == content.data) { 245 | ngx_log_error(clcf->error_log, r->connection->log, 0, 246 | "sass failed to allocate response buffer"); 247 | sass_delete_file_context(ctx_file); 248 | 249 | return NGX_HTTP_INTERNAL_SERVER_ERROR; 250 | } 251 | 252 | ngx_cpystrn(content.data, (unsigned char *) output, strlen(output)); 253 | 254 | b->start = b->pos = content.data; 255 | b->last = b->end = content.data + strlen(output) - 1; 256 | b->memory = 1; 257 | b->last_buf = 1; 258 | 259 | r->headers_out.status = NGX_HTTP_OK; 260 | r->headers_out.content_type = ngx_http_sass_type; 261 | r->headers_out.content_length_n = strlen(output) - 1; 262 | 263 | sass_delete_file_context(ctx_file); 264 | ngx_http_send_header(r); 265 | 266 | return ngx_http_output_filter(r, &out); 267 | } 268 | 269 | 270 | static void * 271 | ngx_http_sass_create_loc_conf(ngx_conf_t *cf) 272 | { 273 | ngx_http_sass_loc_conf_t *conf; 274 | 275 | conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_sass_loc_conf_t)); 276 | 277 | if (NULL == conf) { 278 | return NULL; 279 | } 280 | 281 | conf->enable = NGX_CONF_UNSET; 282 | conf->error_log = NGX_LOG_ERR; 283 | conf->output_style = SASS_STYLE_NESTED; 284 | conf->precision = NGX_CONF_UNSET_UINT; 285 | conf->source_comments = NGX_CONF_UNSET; 286 | conf->source_map_embed = NGX_CONF_UNSET; 287 | 288 | return conf; 289 | } 290 | 291 | 292 | static char * 293 | ngx_http_sass_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 294 | { 295 | ngx_http_sass_loc_conf_t *prev = parent; 296 | ngx_http_sass_loc_conf_t *conf = child; 297 | 298 | ngx_conf_merge_off_value(conf->enable, prev->enable, 0); 299 | ngx_conf_merge_str_value(conf->include_path, prev->include_path, ""); 300 | ngx_conf_merge_str_value(conf->indent, prev->indent, " "); 301 | ngx_conf_merge_off_value(conf->is_indented_syntax, prev->is_indented_syntax, 0); 302 | ngx_conf_merge_str_value(conf->linefeed, prev->linefeed, "\n"); 303 | ngx_conf_merge_uint_value(conf->output_style, prev->output_style, SASS_STYLE_NESTED); 304 | ngx_conf_merge_uint_value(conf->precision, prev->precision, 5); 305 | ngx_conf_merge_off_value(conf->source_comments, prev->source_comments, 0); 306 | ngx_conf_merge_off_value(conf->source_map_embed, prev->source_map_embed, 0); 307 | 308 | return NGX_CONF_OK; 309 | } 310 | 311 | 312 | static char * 313 | ngx_http_sass_error_log_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 314 | { 315 | ngx_str_t *value; 316 | ngx_uint_t n; 317 | 318 | ngx_http_sass_loc_conf_t *slcf = conf; 319 | 320 | value = cf->args->elts; 321 | 322 | for (n = 1; n <= NGX_LOG_DEBUG; n++) { 323 | if (0 == ngx_strcmp(value[1].data, err_levels[n].data)) { 324 | slcf->error_log = n; 325 | return NGX_CONF_OK; 326 | } 327 | } 328 | 329 | ngx_conf_log_error( 330 | NGX_LOG_EMERG, cf, 0, 331 | "invalid sass_error_log parameter \"%V\"", &value[1] 332 | ); 333 | 334 | return NGX_CONF_ERROR; 335 | } 336 | 337 | 338 | static char * 339 | ngx_http_sass_output_value(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) 340 | { 341 | ngx_str_t *value; 342 | 343 | ngx_http_sass_loc_conf_t *slcf = conf; 344 | 345 | value = cf->args->elts; 346 | 347 | if (0 == ngx_strcmp(value[1].data, "nested")) { 348 | slcf->output_style = SASS_STYLE_NESTED; 349 | return NGX_CONF_OK; 350 | } 351 | 352 | if (0 == ngx_strcmp(value[1].data, "expanded")) { 353 | slcf->output_style = SASS_STYLE_EXPANDED; 354 | return NGX_CONF_OK; 355 | } 356 | 357 | if (0 == ngx_strcmp(value[1].data, "compact")) { 358 | slcf->output_style = SASS_STYLE_COMPACT; 359 | return NGX_CONF_OK; 360 | } 361 | 362 | if (0 == ngx_strcmp(value[1].data, "compressed")) { 363 | slcf->output_style = SASS_STYLE_COMPRESSED; 364 | return NGX_CONF_OK; 365 | } 366 | 367 | ngx_conf_log_error( 368 | NGX_LOG_EMERG, cf, 0, 369 | "invalid sass_output parameter \"%V\"", &value[1] 370 | ); 371 | 372 | return NGX_CONF_ERROR; 373 | } 374 | 375 | 376 | static ngx_int_t 377 | ngx_http_sass_init(ngx_conf_t *cf) 378 | { 379 | ngx_http_handler_pt *h; 380 | ngx_http_core_main_conf_t *cmcf; 381 | 382 | cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); 383 | h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); 384 | 385 | if (NULL == h) { 386 | return NGX_ERROR; 387 | } 388 | 389 | *h = ngx_http_sass_handler; 390 | 391 | return NGX_OK; 392 | } 393 | -------------------------------------------------------------------------------- /t/conf_include-path.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | my $include_dir = File::Spec->catdir($test_dir, 'includes'); 9 | 10 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 11 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 12 | 13 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 14 | $ENV{TEST_NGINX_INCLUDE_DIR} = $include_dir; 15 | 16 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 17 | read($fh, our $http_config, -s $fh); 18 | close $fh; 19 | 20 | # proceed with testing 21 | repeat_each(2); 22 | plan tests => repeat_each() * blocks() * 2; 23 | 24 | no_root_location(); 25 | run_tests(); 26 | 27 | __DATA__ 28 | 29 | === TEST 1: custom include path 30 | --- main_config eval: $::http_config 31 | --- config 32 | location /conf_include-path.scss { 33 | root $TEST_NGINX_FIXTURE_DIR; 34 | 35 | sass_compile on; 36 | sass_include_path $TEST_NGINX_INCLUDE_DIR; 37 | } 38 | --- request 39 | GET /conf_include-path.scss 40 | --- response_body_like: background-color: "black" 41 | -------------------------------------------------------------------------------- /t/conf_indent.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default indent 28 | --- main_config eval: $::http_config 29 | --- config 30 | location /default.scss { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /default.scss 40 | --- response_body 41 | body { 42 | background-color: white; 43 | color: black; } 44 | 45 | === TEST 2: custom indent 46 | --- main_config eval: $::http_config 47 | --- config 48 | location /default.scss { 49 | root $TEST_NGINX_FIXTURE_DIR; 50 | 51 | sass_compile on; 52 | sass_indent " "; 53 | 54 | header_filter_by_lua_block { ngx.header.content_length = nil } 55 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 56 | } 57 | --- request 58 | GET /default.scss 59 | --- response_body 60 | body { 61 | background-color: white; 62 | color: black; } 63 | -------------------------------------------------------------------------------- /t/conf_is-indented-syntax.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default syntax (== not indented) 28 | --- main_config eval: $::http_config 29 | --- config 30 | location ~ ^.*\.scss$ { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /default.scss 40 | --- response_body 41 | body { 42 | background-color: white; 43 | color: black; } 44 | 45 | === TEST 2: indented syntax "off" 46 | --- main_config eval: $::http_config 47 | --- config 48 | location ~ ^.*\.scss$ { 49 | root $TEST_NGINX_FIXTURE_DIR; 50 | 51 | sass_compile on; 52 | sass_is_indented_syntax off; 53 | 54 | header_filter_by_lua_block { ngx.header.content_length = nil } 55 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 56 | } 57 | --- request 58 | GET /default.scss 59 | --- response_body 60 | body { 61 | background-color: white; 62 | color: black; } 63 | 64 | === TEST 3: indented syntax "on" 65 | --- main_config eval: $::http_config 66 | --- config 67 | location ~ ^.*\.sass$ { 68 | root $TEST_NGINX_FIXTURE_DIR; 69 | 70 | sass_compile on; 71 | sass_is_indented_syntax on; 72 | 73 | header_filter_by_lua_block { ngx.header.content_length = nil } 74 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 75 | } 76 | --- request 77 | GET /conf_is-indented-syntax.sass 78 | --- response_body 79 | body { 80 | background-color: white; 81 | color: black; } 82 | -------------------------------------------------------------------------------- /t/conf_linefeed.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default linefeed 28 | --- main_config eval: $::http_config 29 | --- config 30 | location /default.css { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | rewrite ^(.*)\.css$ $1.scss break; 36 | 37 | header_filter_by_lua_block { ngx.header.content_length = nil } 38 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 39 | } 40 | --- request 41 | GET /default.css 42 | --- response_body 43 | body { 44 | background-color: white; 45 | color: black; } 46 | 47 | === TEST 2: custom linefeed 48 | --- main_config eval: $::http_config 49 | --- config 50 | location /default.scss { 51 | root $TEST_NGINX_FIXTURE_DIR; 52 | 53 | sass_compile on; 54 | sass_linefeed "\n/* linefeed */\n"; 55 | 56 | header_filter_by_lua_block { ngx.header.content_length = nil } 57 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 58 | } 59 | --- request 60 | GET /default.scss 61 | --- response_body 62 | body { 63 | /* linefeed */ 64 | background-color: white; 65 | /* linefeed */ 66 | color: black; } 67 | /* linefeed */ 68 | -------------------------------------------------------------------------------- /t/conf_output-style.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default output style 28 | --- main_config eval: $::http_config 29 | --- config 30 | location ~ ^.*\.scss$ { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /conf_output-style.scss 40 | --- response_body 41 | .output { 42 | background-color: white; } 43 | .output .with-style { 44 | color: black; } 45 | 46 | === TEST 2: output style "compact" 47 | --- main_config eval: $::http_config 48 | --- config 49 | location ~ ^.*\.scss$ { 50 | root $TEST_NGINX_FIXTURE_DIR; 51 | 52 | sass_compile on; 53 | sass_output_style compact; 54 | 55 | header_filter_by_lua_block { ngx.header.content_length = nil } 56 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 57 | } 58 | --- request 59 | GET /conf_output-style.scss 60 | --- response_body 61 | .output { background-color: white; } 62 | 63 | .output .with-style { color: black; } 64 | 65 | === TEST 3: output style "compressed" 66 | --- main_config eval: $::http_config 67 | --- config 68 | location ~ ^.*\.scss$ { 69 | root $TEST_NGINX_FIXTURE_DIR; 70 | 71 | sass_compile on; 72 | sass_output_style compressed; 73 | 74 | header_filter_by_lua_block { ngx.header.content_length = nil } 75 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 76 | } 77 | --- request 78 | GET /conf_output-style.scss 79 | --- response_body 80 | .output{background-color:#fff}.output .with-style{color:#000} 81 | 82 | === TEST 4: output style "expanded" 83 | --- main_config eval: $::http_config 84 | --- config 85 | location ~ ^.*\.scss$ { 86 | root $TEST_NGINX_FIXTURE_DIR; 87 | 88 | sass_compile on; 89 | sass_output_style expanded; 90 | 91 | header_filter_by_lua_block { ngx.header.content_length = nil } 92 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 93 | } 94 | --- request 95 | GET /conf_output-style.scss 96 | --- response_body 97 | .output { 98 | background-color: white; 99 | } 100 | 101 | .output .with-style { 102 | color: black; 103 | } 104 | 105 | === TEST 5: output style "nested" 106 | --- main_config eval: $::http_config 107 | --- config 108 | location ~ ^.*\.scss$ { 109 | root $TEST_NGINX_FIXTURE_DIR; 110 | 111 | sass_compile on; 112 | sass_output_style nested; 113 | 114 | header_filter_by_lua_block { ngx.header.content_length = nil } 115 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 116 | } 117 | --- request 118 | GET /conf_output-style.scss 119 | --- response_body 120 | .output { 121 | background-color: white; } 122 | .output .with-style { 123 | color: black; } 124 | -------------------------------------------------------------------------------- /t/conf_precision.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default precision 28 | --- main_config eval: $::http_config 29 | --- config 30 | location ~ ^.*\.scss$ { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /conf_precision.scss 40 | --- response_body 41 | .precision-element { 42 | width: 1.95312px; } 43 | 44 | === TEST 2: custom precision 45 | --- main_config eval: $::http_config 46 | --- config 47 | location ~ ^.*\.scss$ { 48 | root $TEST_NGINX_FIXTURE_DIR; 49 | 50 | sass_compile on; 51 | sass_precision 3; 52 | 53 | header_filter_by_lua_block { ngx.header.content_length = nil } 54 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 55 | } 56 | --- request 57 | GET /conf_precision.scss 58 | --- response_body 59 | .precision-element { 60 | width: 1.953px; } 61 | -------------------------------------------------------------------------------- /t/conf_source-comments.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default comments 28 | --- main_config eval: $::http_config 29 | --- config 30 | location ~ ^.*\.scss$ { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /conf_source-comments.scss 40 | --- response_body 41 | html { 42 | background-color: black; } 43 | 44 | body { 45 | color: white; } 46 | 47 | === TEST 2: comments "off" 48 | --- main_config eval: $::http_config 49 | --- config 50 | location ~ ^.*\.scss$ { 51 | root $TEST_NGINX_FIXTURE_DIR; 52 | 53 | sass_compile on; 54 | sass_source_comments off; 55 | 56 | header_filter_by_lua_block { ngx.header.content_length = nil } 57 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 58 | } 59 | --- request 60 | GET /conf_source-comments.scss 61 | --- response_body 62 | html { 63 | background-color: black; } 64 | 65 | body { 66 | color: white; } 67 | 68 | === TEST 3: comments "on" 69 | --- main_config eval: $::http_config 70 | --- config 71 | location ~ ^.*\.scss$ { 72 | root $TEST_NGINX_FIXTURE_DIR; 73 | 74 | sass_compile on; 75 | sass_source_comments on; 76 | } 77 | --- request 78 | GET /conf_source-comments.scss 79 | --- response_body_like: /* line 1, 80 | -------------------------------------------------------------------------------- /t/conf_source-map-embed.t: -------------------------------------------------------------------------------- 1 | use File::Basename; 2 | use File::Spec; 3 | use Test::Nginx::Socket; 4 | 5 | # setup testing environment 6 | my $test_dir = File::Spec->rel2abs(dirname(__FILE__)); 7 | my $fixture_dir = File::Spec->catdir($test_dir, 'fixtures'); 8 | 9 | my $fixture_config = (defined $ENV{DYNAMIC}) ? '_nginx-dynamic.conf' : '_nginx-static.conf'; 10 | my $fixture_http = File::Spec->catfile($fixture_dir, $fixture_config); 11 | 12 | $ENV{TEST_NGINX_FIXTURE_DIR} = $fixture_dir; 13 | 14 | open(my $fh, '<', $fixture_http) or die "cannot open < $fixture_http: $!"; 15 | read($fh, our $http_config, -s $fh); 16 | close $fh; 17 | 18 | # proceed with testing 19 | repeat_each(2); 20 | plan tests => repeat_each() * blocks() * 2; 21 | 22 | no_root_location(); 23 | run_tests(); 24 | 25 | __DATA__ 26 | 27 | === TEST 1: default source map embed 28 | --- main_config eval: $::http_config 29 | --- config 30 | location /default.scss { 31 | root $TEST_NGINX_FIXTURE_DIR; 32 | 33 | sass_compile on; 34 | 35 | header_filter_by_lua_block { ngx.header.content_length = nil } 36 | body_filter_by_lua_block { ngx.arg[1] = ngx.arg[1] .. "\n" } 37 | } 38 | --- request 39 | GET /default.scss 40 | --- response_body 41 | body { 42 | background-color: white; 43 | color: black; } 44 | 45 | === TEST 2: source map embed "on" 46 | --- main_config eval: $::http_config 47 | --- config 48 | location /default.scss { 49 | root $TEST_NGINX_FIXTURE_DIR; 50 | 51 | sass_compile on; 52 | sass_source_map_embed on; 53 | } 54 | --- request 55 | GET /default.scss 56 | --- response_body_like: sourceMappingURL=data:application/json;base64 57 | -------------------------------------------------------------------------------- /t/fixtures/_nginx-dynamic.conf: -------------------------------------------------------------------------------- 1 | # base directory: "t/servroot" 2 | load_module '../../vendor/ngx_http_sass_module.so'; 3 | -------------------------------------------------------------------------------- /t/fixtures/_nginx-static.conf: -------------------------------------------------------------------------------- 1 | # intentionally left blank 2 | -------------------------------------------------------------------------------- /t/fixtures/conf_include-path.scss: -------------------------------------------------------------------------------- 1 | @import "_test"; 2 | 3 | body { 4 | background-color: $color-include; 5 | } 6 | -------------------------------------------------------------------------------- /t/fixtures/conf_is-indented-syntax.sass: -------------------------------------------------------------------------------- 1 | $color-background: white; 2 | $color-text: black; 3 | 4 | body 5 | background-color: $color-background; 6 | color: $color-text; 7 | -------------------------------------------------------------------------------- /t/fixtures/conf_output-style.scss: -------------------------------------------------------------------------------- 1 | $color-background: white; 2 | $color-text: black; 3 | 4 | .output { 5 | background-color: $color-background; 6 | 7 | .with-style { 8 | color: $color-text; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /t/fixtures/conf_precision.scss: -------------------------------------------------------------------------------- 1 | $pwidth: (20 * 100) / 1024 * 1px; 2 | 3 | .precision-element { 4 | width: $pwidth; 5 | } 6 | -------------------------------------------------------------------------------- /t/fixtures/conf_source-comments.scss: -------------------------------------------------------------------------------- 1 | html { 2 | background-color: black; 3 | } 4 | 5 | body { 6 | color: white; 7 | } 8 | -------------------------------------------------------------------------------- /t/fixtures/default.scss: -------------------------------------------------------------------------------- 1 | $color-background: white; 2 | $color-text: black; 3 | 4 | body { 5 | background-color: $color-background; 6 | color: $color-text; 7 | } 8 | -------------------------------------------------------------------------------- /t/includes/_test.scss: -------------------------------------------------------------------------------- 1 | $color-include: "black"; 2 | --------------------------------------------------------------------------------