Skip to content

Instantly share code, notes, and snippets.

@doiftrue
Last active December 21, 2024 19:55
Show Gist options
  • Save doiftrue/d975646f41f218499866968a1a7dbf62 to your computer and use it in GitHub Desktop.
Save doiftrue/d975646f41f218499866968a1a7dbf62 to your computer and use it in GitHub Desktop.
WP_Theme_Scripts_Styles - simple class to enqueue scripts and styles using config array.
<?php
Theme_Scripts_Styles::init( theme_scripts_styles_callback(...), [ 'defer_all_scripts' => true ] );
/**
* @see Theme_Scripts_Styles
*
* @return array{scripts:array, styles:array}
*/
function theme_scripts_styles_callback(): array {
$styles = [
'dashicons' => [
'url' => site_url( $GLOBALS['wp_styles']->query('dashicons')->src ),
'ver' => $GLOBALS['wp_version'],
//'deps' => [ 'theme-styles' ],
'preload' => true,
],
'fonts' => [
'url' => THEME_URL . ( $f = '/assets/fonts/roboto.font.css' ),
'ver' => filemtime( THEME_DIR . $f ),
'preload' => true,
],
'theme-styles' => [
'url' => THEME_URL . ( $f = '/assets/styles/styles.min.css' ),
'ver' => filemtime( THEME_DIR . $f ),
'deps' => [ 'dashicons', 'fonts' ], // после dashicons
'preload' => true,
],
];
$scripts = [
'jquery' => [
'url' => site_url( $GLOBALS['wp_scripts']->query('jquery-core')->src ),
'ver' => $GLOBALS['wp_scripts']->query('jquery-core')->ver,
],
'theme-scripts-libs' => [
'url' => THEME_URL . ( $f = '/assets/js/scripts-libs.min.js' ),
'ver' => filemtime( THEME_DIR . $f ),
],
'alpine' => [
'url' => THEME_URL . ( $f = '/assets/js/libs/alpinejs/alpine.min.js' ),
'ver' => filemtime( THEME_DIR . $f ),
'defer' => true,
],
'theme-scripts' => [
'url' => THEME_URL . ( $f = '/assets/js/scripts.min.js' ),
'ver' => filemtime( THEME_DIR . $f ),
'deps' => [ 'jquery', 'theme-scripts-libs' ],
/* @javascript {object} kmjsdata */
'localize' => [
'kmjsdata' => [
'spdelcook' => KILL_SAPE_COOKIE,
'jsl10n' => js_l10n_strings(),
]
],
],
];
// jquery-ui-sortable
if( is_user_logged_in() ){
$sortable = $GLOBALS['wp_scripts']->query('jquery-ui-sortable');
$scripts[ 'jquery-ui-sortable' ] = [
'url' => site_url( $sortable->src ),
'ver' => $GLOBALS['wp_version'],
'deps' => $sortable->deps,
];
}
return [
'scripts' => $scripts,
'styles' => $styles,
];
}
<?php
$data_callback = function() {
return [
'styles' => [
'fonts' => [
'url' => ASSETS_URL . ( $f = '/fonts/theme-fonts.css' ),
'ver' => filemtime( ASSETS_DIR . $f ),
'preload' => true,
'load_scope' => 'front,admin', // "admin", "front", "front,admin"
],
'theme-styles' => [
'url' => ASSETS_URL . ( $f = '/scss/styles.min.css' ),
'ver' => filemtime( ASSETS_DIR . $f ),
'deps' => [],
'preload' => true,
],
],
'scripts' => [
'theme-scripts-libs' => [
'url' => ASSETS_URL . ( $f = '/js/scripts-libs.min.js' ),
'ver' => filemtime( ASSETS_DIR . $f ),
],
'theme-scripts' => [
'url' => ASSETS_URL . ( $f = '/js/scripts.min.js' ),
'ver' => filemtime( ASSETS_DIR . $f ),
'deps' => [ 'theme-scripts-libs' ],
'localize' => [
'globaljsdata' => [
'spdelcook' => KILL_SAPE_COOKIE,
'jsl10n' => 'some data here...',
]
],
],
]
];
};
Theme_Scripts_Styles::init( $data_callback );
<?php
/**
* @author Kama (wp-kama.com)
* @see https://gist.github.com/doiftrue/d975646f41f218499866968a1a7dbf62
*
* @version 2.4
*/
class Theme_Scripts_Styles {
/**
* Available parameters.
*/
private static array $default_item_args = [
'url' => '',
'ver' => '',
'deps' => [],
'load_scope' => 'front', // where to load asset: One of: "admin", "front", "front,admin"
'preload' => false, // bool
'prefetch' => false, // bool
'dns-prefetch' => false, // bool
'preconnect' => false, // bool
// js only
'async' => false, // bool
'defer' => false, // bool
'module' => false, // bool
/**
* Array of data to pass to {@see wp_localize_script()}
* The key becomes $object_name parameter and the value $i18n.
* Example:
*
* 'localize' => [
* 'object_name' => [
* 'hello' => 'Hallo Welt',
* ]
* ]
*/
'localize' => [],
/**
* Array of data to render before script as json.
* Example:
*
* 'js_data' => [
* 'object_name' => [
* 'my_number' => 654,
* 'is_enabled' => true,
* 'my_value' => 'Hello',
* ]
* ]
*/
'js_data' => [],
];
private static array $opt; // init parameters
/** @var array{scripts:array, styles:array} */
private static array $data;
/** @var callable */
private static $data_callback;
/**
* @param callable $data_callback The callback whould return array{scripts:array, styles:array}
* @param array $options {
* @type bool $force_to_footer Default: true.
* }
*/
public static function init( callable $data_callback, array $options = [] ): void {
self::$opt = $options + [
'force_to_footer' => true,
'defer_all_scripts' => false,
];
self::$data_callback = $data_callback;
add_action( 'wp_enqueue_scripts', [ __CLASS__, 'enqueue_scripts_styles' ], 99 );
add_action( 'admin_enqueue_scripts', [ __CLASS__, 'enqueue_scripts_styles' ], 99 );
add_filter( 'script_loader_tag', [ __CLASS__, '_set_async_defer_attrs' ], 10, 2 );
add_action( 'send_headers', [ __CLASS__, 'set_prefetch_headers__on_send_headers' ] );
// all scripts to the footer
if( self::$opt['force_to_footer'] ){
remove_action( 'wp_head', 'wp_print_head_scripts', 9 );
}
// defer all scripts
if( self::$opt['defer_all_scripts'] && ! is_admin() ){
add_filter( 'script_loader_tag', [ __CLASS__, 'defer_all_scripts' ], 20 );
}
}
public static function defer_all_scripts( $tag ){
if( ! preg_match( '/ type="module"|async |defer /', $tag ) ){
return str_replace( ' src', ' defer src', $tag );
}
return $tag;
}
private static function setup_data_once(): void {
if( isset( self::$data ) ){
return; // already set
}
self::$data = ( self::$data_callback )();
// fill defaults
self::$data['scripts'] = array_map( static fn( $dat ) => $dat + self::$default_item_args, self::$data['scripts'] );
self::$data['styles'] = array_map( static fn( $dat ) => $dat + self::$default_item_args, self::$data['styles'] );
// filter by load_scope
$cur_scope = is_admin() ? 'admin' : 'front';
foreach( self::$data as $type => $type_data ){
foreach( $type_data as $id => $item_data ){
if( ! str_contains( $item_data['load_scope'], $cur_scope ) ){
unset( self::$data[ $type ][ $id ] );
}
}
}
}
public static function _set_async_defer_attrs( $tag, $id ) {
$data = self::$data['scripts'][ $id ] ?? null;
if( $data ){
$data['module'] && $tag = str_replace( ' src', ' type="module" src', $tag );
$data['async'] && $tag = str_replace( ' src', ' async src', $tag );
$data['defer'] && $tag = str_replace( ' src', ' defer src', $tag );
}
return $tag;
}
public static function set_prefetch_headers__on_send_headers(): void {
if( is_feed() || is_robots() ){
return;
}
self::setup_data_once();
foreach( self::$data['styles'] as $data ){
self::add_prefetch_headers( $data, 'style' );
}
foreach( self::$data['scripts'] as $data ){
self::add_prefetch_headers( $data, 'script' );
}
}
/**
* DOCS: https://html.spec.whatwg.org/#linkTypes
* DOCS: https://html.spec.whatwg.org/#link-type-preload
*/
private static function add_prefetch_headers( $data, $type ): void {
$home_url = rtrim( home_url(), '/' );
$url = str_starts_with( $data['url'], $home_url )
? wp_make_link_relative( $data['url'] )
: $data['url'];
if( $data['ver'] ){
$url .= "?ver={$data['ver']}";
}
if( $data['dns-prefetch'] ){
header( "Link: <$url>; rel=dns-prefetch;", false );
}
elseif( $data['prefetch'] ){
header( "Link: <$url>; rel=prefetch;", false );
}
elseif( $data['preconnect'] ){
header( "Link: <$url>; rel=preconnect;", false );
}
// DOCS: https://www.w3.org/TR/preload/
elseif( $data['preload'] ){
header( "Link: <$url>; rel=preload; as=$type", false );
}
}
public static function enqueue_scripts_styles(): void {
self::setup_data_once();
// scripts
foreach( self::$data['scripts'] as $id => $dat ){
wp_deregister_script( $id ); // remove if registered
wp_enqueue_script( $id, $dat['url'], ( $dat['deps'] ?? [] ), $dat['ver'], true );
if( $dat['localize'] ){
foreach( $dat['localize'] as $obj_name => $l10n ){
wp_localize_script( $id, $obj_name, $l10n );
}
}
if( $dat['js_data'] ){
foreach( $dat['js_data'] as $obj_name => $js_data ){
wp_add_inline_script( $id, "window.$obj_name = " . wp_json_encode( $js_data ), 'before' );
}
}
}
// styles
foreach( self::$data['styles'] as $id => $dat ){
wp_deregister_style( $id ); // remove if registered
wp_enqueue_style( $id, $dat['url'], ( $dat['deps'] ?? [] ), $dat['ver'] );
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment