Code Monkey home page Code Monkey logo

nginx-http-concat's Introduction

nginx-http-concat

Overview

WP.com plugin to perform CSS and Javascript concatenation of individual scripts into a single script.

Installation & Configuration

  1. Copy the ‘http-concat’ directory and its contents to your WordPress plugins directory.

  2. Configure the NGINX server to perform the concatenation step in the process by adding the following to your WordPress installations NGINX configuration:

    location /_static/ {
        fastcgi_pass unix:/var/run/fastcgi.sock;
        include /etc/nginx/fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root/wp-content/plugins/http-concat/ngx-http-concat.php;
    }
  1. Once this is done the installation is ready for use and you can enable/disable the JS and/or CSS concatenation via the plugins interface of your WordPress installation.

nginx-http-concat's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

nginx-http-concat's Issues

Adding Async/Defer to the JSConcat script

There does not seem to be a way to filter the resulting script block. If it could be filtered then attributes could be added to the concatenated script block such as performance related async or defer.

Composer support

Composer is a popular php dependancy manger. Many of automattic plugins already support it, such as jetpack and babble. Supporting it is simple, only requires having a composer.json file in the root of the project and for the repo not using git submodules.

It is extremely useful if you want to load plugins from github 👍

Related #8

Bug with wp_add_inline_script

This is a fun edge case bug:

When a script ("child") is concatenated with another script that it requires ("parent"), but also uses wp_add_inline_script() with the third argument being before, any code that has dependencies on the parent script will fail.

Just as an example:

  1. The Shortcake plugin registers select2 with a dependency on jQuery
  2. It then adds an inline script that depends on jQuery, but wants it output before the select2 JS file.
  3. The nginx-http-concat plugin will output the before script
  4. The nginx-http-concat plugin will output the concatenated blob
  5. If the parent and child script are in the same blob, the before JS will fail.

I believe what needs to be done is to check if a script has a wp_add_inline_script() with a before , it would be good to break the blob apart at that exact script, output the inline script, and then the concatenated blob.

We could get more detailed and check if any parent/child dependencies exist within the same blob first.

Deprecated: parse_url(): Passing null to parameter #1 ($url) of type string is deprecated

After upgrading PHP to 8.1, this plugin is now throwing deprecation warnings.

PHP message: Deprecated: parse_url(): Passing null to parameter #1 ($url) of type string is deprecated in /var/www/wp-content/mu-plugins/http-concat/cssconcat.php on line 58 [REDACTED/reports-data/rail-service-data/] [wp-content/mu-plugins/http-concat/cssconcat.php:58 parse_url(), wp-includes/functions.wp-styles.php:68 WPcom_CSS_Concat->do_items(), wp-includes/class-wp-hook.php:324 wp_print_styles(), wp-includes/class-wp-hook.php:348 WP_Hook->apply_filters(), wp-includes/plugin.php:517 WP_Hook->do_action(), wp-includes/general-template.php:3052 do_action('wp_head'), wp-content/themes/stb/header.php:27 wp_head(), wp-includes/template.php:790 require_once('wp-content/themes/stb/header.php'), wp-includes/template.php:725 load_template(), wp-includes/general-template.php:48 locate_template(), wp-content/themes/stb/index.php:1 get_header(), wp-includes/template-loader.php:106 include('wp-content/themes/stb/index.php'), wp-blog-header.php:19 require_once('wp-includes/template-loader.php'), index.php:17 require('wp-blog-header.php')]

A similar pull request was created to solve this issue for rtrim(): #72

Here is where the error occurs:

$css_url_parsed = parse_url( $obj->src );

$test_url_parsed = parse_url( $test_url );

PHP docs: https://www.php.net/manual/en/function.parse-url.php

As of PHP 8.0.0, parse_url() distinguishes absent and empty queries and fragments:

http://example.com/foo → query = null, fragment = null
http://example.com/foo? → query = "",   fragment = null
http://example.com/foo# → query = null, fragment = ""
http://example.com/foo?# → query = "",   fragment = ""

Previously all cases resulted in query and fragment being null.

Add support for wp_add_inline_script

Since WordPress 4.5, developers can take advantage of wp_add_inline_script function which prints and inline script before or after enqueued script.

I believe that we should either print all related inline scripts before/after a set of concatenated scripts, or exclude those from concatenation, which might lead to a faster resolution for an issue which is affecting live sites ATM.

External scripts are not enqueued with necessary dependencies

do_item() is called to let core handle external scripts, since they don't get concatenated. Unfortunately, $this->do_concat is true, so the external script tag is saved for later instead of being echo'd at the proper time for its dependents/dependencies

I've got a patch (below) but I'm not 100% sure enough to know that it wouldn't break other things. It likely needs cleaned up and tested more.

diff --git a/jsconcat.php b/jsconcat.php
index b46a4b5..ae8c61a 100644
--- a/jsconcat.php
+++ b/jsconcat.php
@@ -39,6 +39,7 @@ class WPcom_JS_Concat extends WP_Scripts {
                $handles = false === $handles ? $this->queue : (array) $handles;
                $javascripts= array();
                $siteurl = site_url();
+               $external_scripts = array();

                $this->all_deps( $handles );
                $level = 0;
@@ -73,8 +74,10 @@ class WPcom_JS_Concat extends WP_Scripts {
                                $do_concat = true;

                        // Don't try to concat externally hosted scripts
-                       if ( ( isset( $js_url['host'] ) && ( preg_replace( '/https?:\/\//', '', $siteurl ) != $js_url['host'] ) ) )
+                       if ( ( isset( $js_url['host'] ) && ( preg_replace( '/https?:\/\//', '', $siteurl ) != $js_url['host'] ) ) ) {
+                               $external_scripts[] = $handle;
                                $do_concat = false;
+                       }

                        // Concat and canonicalize the paths only for
                        // existing scripts that aren't outside ABSPATH
@@ -114,8 +117,17 @@ class WPcom_JS_Concat extends WP_Scripts {

                foreach ( $javascripts as $js_array ) {
                        if ( 'do_item' == $js_array['type'] ) {
-                               if ( $this->do_item( $js_array['handle'], $group ) )
+                               // Disable "concat" for external scripts, otherwise do_item() may print it at the wrong time
+                               if ( in_array( $js_array['handle'], $external_scripts, true ) ) {
+                                       $this->do_concat = false;
+                               }
+                               if ( $this->do_item( $js_array['handle'], $group ) ) {
                                        $this->done[] = $js_array['handle'];
+                               }
+                               // Re-enable "concat"
+                               if ( in_array( $js_array['handle'], $external_scripts, true ) ) {
+                                       $this->do_concat = true;
+                               }
                        } else if ( 'concat' == $js_array['type'] ) {
                                array_map( array( $this, 'print_extra_script' ), $js_array['handles'] );

How to install this

We copy the main folder and rename it http-concat and put it inside /plugins/ folder on wp?

PHP Notices raised by cssconcat

When running unit tests for Customize Widgets Plus inside of Quickstart, I'm noticing PHP Notices being raised:

Only variable references should be returned by reference

/srv/www/wp-content/mu-plugins/http-concat/cssconcat.php:174
/srv/www/wp/wp-includes/class.wp-dependencies.php:219
/srv/www/wp/wp-includes/script-loader.php:653
/srv/www/wp/wp-includes/plugin.php:579
/srv/www/wp/wp-includes/class.wp-styles.php:39
/srv/www/wp-content/mu-plugins/http-concat/cssconcat.php:31
/srv/www/wp-content/mu-plugins/http-concat/cssconcat.php:191
/srv/www/wp/wp-includes/plugin.php:503
/srv/www/wp-tests/tests/phpunit/tests/customize/widgets.php:53
/srv/www/wp-tests/tests/phpunit/tests/customize/widgets.php:175

The code in question is:

class WPcom_CSS_Concat extends WP_Styles {
    // ...
    function &__get( $key ) {
        return $this->old_styles->$key;
    }
    // ...
}

I tried changing this method to:

    function &__get( $key ) {
        $value = $this->old_styles->$key;
        return $value;
    }

But then of course a different error is raised when wp_styles()->registered[ $key ] is attempted to be set:

Indirect modification of overloaded property WPcom_CSS_Concat::$registered has no effect

/srv/www/wp/wp-includes/class.wp-dependencies.php:221
/srv/www/wp/wp-includes/functions.wp-styles.php:116
/srv/www/wp-content/plugins/jetpack/_inc/genericons.php:11
/srv/www/wp/wp-includes/plugin.php:503
/srv/www/wp-tests/tests/phpunit/tests/customize/widgets.php:53
/srv/www/wp-tests/tests/phpunit/tests/customize/widgets.php:175

I think perhaps the approach for extending WP_Styles needs to be changed. Instead of storing the original instance of WP_Styles inside of the WPcom_CSS_Concat instance, I think it should be fully re-using all of its properties directly.

Bug in concatenation

There seems to be a bug in the concatenation code surrounding charsets, which adds a character that prevents files from downloading correctly:

When concatenating files, if any of them has a BOM present (or one of the files is UTF-8 encoded or contains unicode), the BOM should be stripped, prepended to the output, and the encoding headers updated.

Each file is modified from here:

$buf = file_get_contents( $fullpath );

What’s needed is a check for a BOM, and if found, replace it with an empty string, and set a flag, and if that flag is found, prepend the BOM on the final output.

Pass unique and relevant $handle to style_loader_tag filter

In https://github.com/Automattic/nginx-http-concat/blob/79114ddd7755a34d7e356797622d2859e5508cfa/cssconcat.php#L132we're passing in $handle which is set only b/c of a foreach loop in

foreach( $this->to_do as $key => $handle ) {
which is totally unrelated.

This may cause issues, for instance in combination with Jetpack, which is returning an empty string in case the $handle matches an array https://github.com/Automattic/jetpack/blob/cfa6a99e9898b3090898723868b58c9796a06834/class.jetpack.php#L6151

Lots of warnings generated

I tried activating the plugins with a VIP theme activated, and I got a lot of warnings:

  • Warning: Creating default object from empty value in …http-concat/cssconcat.php 176
  • Warning: Creating default object from empty value in …http-concat/jsconcat.php on line 173
  • Warning: in_array() expects parameter 2 to be array, null given in …/class.wp-dependencies.php on line 168

Duplicate ID defined in some instances

This ID:

echo apply_filters( 'ngx_http_concat_style_loader_tag', "<link rel='stylesheet' id='$media-css-$idx' href='$href' type='text/css' media='$media' />\n", $handles, $href, $media );

the do_items() function is called twice, once for the head, once for the footer. This means the loop that sets the number in the ID is run twice, resetting on the second instance, resulting in the doubled ID.

Simplest fix might be to increment the number sufficiently on one of the loops. e.g. adding between these lines

foreach( $stylesheets as $idx => $stylesheets_group ) {
foreach( $stylesheets_group as $media => $css ) {

 if ( $group ) { $idx += 1000; }

ini_get ALWAYS returns a string

ini_get always returns a string. In cssmin.php:do_raise_php_limits, there is this code:

            // memory_limit exception: allow -1 for "no memory limit".
            if ($current > -1 && ($suggested == -1 || $current < $suggested)) {
                ini_set($name, $suggested);
            }

and then in normalize_int this code block:

        if (is_string($size)) {
            switch (substr($size, -1)) {
       ....

The is_string doesn't make sense because ini_get always returns a string, which then produces a NOTICE because you're trying to substring a number.

normalize_int should use is_numeric instead of is_string

Group common scripts

Concat can be a little overzealous in cases where a script or style is enqueued conditionally. Say a script is used on one page. Ideally we wouldn't serve a different Javascript bundle on that page that is the same as every other page with the one extra script added. The bundle that loads on every other page is most likely already cached in the user's browser. We really just need to serve the additional script separately (or maybe inline?).

When WP_CONTENT_DIR is changed WPCOM_Concat_Utils::realpath fails

Related to #28.

When you separate your WordPress install and the wp-content directory via WP_CONTENT_DIR realpath fails and will never concatenate your theme and plugin scripts. I started working on a patch but found that even when I got realpath to account for this set-up line 103 in jsconcat.php has conditionals that continue to break in this kind of set-up.

I discovered this in a multisite sub-domain install configuration, haven't tested on the default set-up.

Gutenberg's `register_block_style()` not working due to concatenation

Describe the bug
On VIP Go (which uses this package as a submodule in https://github.com/Automattic/vip-go-mu-plugins), it is not possible to register block styles for the Gutenberg editor with register_block_style().

To Reproduce
Steps to reproduce the behavior:

  1. Create a system on VIP Go
  2. Write a plugin/theme that uses register_block_style()
  3. Deploy to VIP Go
  4. See that the block styles are not present

Alternatively, it should be possible to reproduce the issue locally with these steps:

  1. Set VIP_GO_ENABLE_HTTP_CONCAT to true
  2. Write a plugin/theme that uses register_block_style()
  3. See that the block styles are not present

Expected behavior
Block styles should be present.

Additional context
This is caused because the concatenation "messes up" script handles and therefore wp_add_inline_script() calls that depend on the existence of handles.

According to the VIP Go documentation, it should be possible to filter (disable) concatenation for individual handles with the js_do_concat filter. However, it looks like nginx-http-concat does not properly handle wp_register_script() calls if $src is false, i.e. if it is an alias for the script it depends on. Apparently, these "alias handles" are not passed through the js_do_concat filter, making it impossible to use.

This is the case for block styles (https://github.com/WordPress/WordPress/blob/9d2315b4097cd58a60b48f17d7f269487665d239/wp-includes/script-loader.php#L2354-L2356), with $inline_script being the block styles object:

wp_register_script( 'wp-block-styles', false, array( 'wp-blocks' ), true, true );
wp_add_inline_script( 'wp-block-styles', $inline_script );
wp_enqueue_script( 'wp-block-styles' );

It should be mentioned that disabling concatenation with the URL parameter concat_js=false will make the Block Styles work.

Can you confirm this? Thanks!

When a group is enqueued, localized scripts are missed

Grazia has this issue with their mediaelement JS, where localized data is not output during JS concat.

The symptom is that their video embeds have black bars.

It's possible this issue affects code elsewhere with a similar pattern, which is when a group of scripts is enqueued without a JS url, and localized data is also set using the same group handle.

Here is an example pattern that will reproduce this issue, in this case from WP core wp-includes/script-loader.php:

$scripts->add( 'mediaelement', false, array( 'jquery', 'mediaelement-core', 'mediaelement-migrate' ), '4.2.6-78496d1' );
...
did_action( 'init' ) && $scripts->localize(
		'mediaelement', '_wpmejsSettings',
		/**
		 * Filters the MediaElement configuration settings.
		 *
		 * @since 4.4.0
		 *
		 * @param array $mejs_settings MediaElement settings array.
		 */
		apply_filters( 'mejs_settings', $mejs_settings )
	);

The mediaelement group contains no src attribute but does contain the extra / data attribute

(
    [handle] => mediaelement
    [src] =>
    [deps] => Array(
        [0] => jquery
        [1] => mediaelement-core
        [2] => mediaelement-migrate
    )
    [ver] => 4.2.6-78496d1
    [args] =>
    [extra] => Array(
        [data] => var _wpmejsSettings = {"pluginPath":"\/wp-includes\/js\/mediaelement\/","classPrefix":"mejs-","stretching":"responsive"};
    )
 )

At line 52 of jsconcat, however, the handle is skipped because it has an empty src

50:			if ( ! $this->registered[$handle]->src ) { // Defines a group.
51:				$this->done[] = $handle;
52:				continue;
53:			}

As a result, the settings var is missing, and the media element is not "responsive".

I'll fix this with a pull request shortly, adding after line 50:

$this->print_extra_script( $handle );

I've tested it on Grazia sandboxed and it results in the correct settings var being set.

Props to @mdbitz for narrowing the issue down to the JS concat.

Multisite (subfolders) doesn't work.

Hey

Cool plugin, works well for our root domain, however no "??"-urls are generated for domains residing in subfolders.

I'm guessing the current version doesn't support it?

Skip concat if logged in as admin

This works great - however I use the Avada theme, and when this plugin is active, it breaks the Fusion Builder editor. Is there a way to apply logic that says if a user is logged into the back end, then do not concat / bypass?

Or, bypass concat anywhere inside wp-admin.

Thanks

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.