Some checks failed
Standards Compliance / Secret Scanning (push) Successful in 3s
Standards Compliance / License Header Validation (push) Successful in 4s
Standards Compliance / Repository Structure Validation (push) Successful in 5s
Standards Compliance / Coding Standards Check (push) Failing after 3s
Standards Compliance / Version Consistency Check (push) Successful in 3s
Standards Compliance / Workflow Configuration Check (push) Failing after 2s
Standards Compliance / Documentation Quality Check (push) Successful in 3s
Standards Compliance / README Completeness Check (push) Successful in 3s
Standards Compliance / Git Repository Hygiene (push) Successful in 2s
Standards Compliance / Script Integrity Validation (push) Successful in 4s
Standards Compliance / Line Length Check (push) Failing after 4s
Standards Compliance / File Naming Standards (push) Successful in 2s
Standards Compliance / Insecure Code Pattern Detection (push) Successful in 3s
Standards Compliance / Code Complexity Analysis (push) Successful in 3s
Standards Compliance / Code Duplication Detection (push) Successful in 4s
Standards Compliance / Dead Code Detection (push) Successful in 3s
Standards Compliance / File Size Limits (push) Successful in 2s
CodeQL Security Scanning / Analyze (javascript) (push) Failing after 1m9s
Standards Compliance / Binary File Detection (push) Successful in 4s
CodeQL Security Scanning / Analyze (actions) (push) Failing after 1m11s
Standards Compliance / TODO/FIXME Tracking (push) Successful in 3s
Standards Compliance / Dependency Vulnerability Scanning (push) Successful in 5s
Standards Compliance / Broken Link Detection (push) Successful in 5s
Standards Compliance / Unused Dependencies Check (push) Successful in 7s
Standards Compliance / API Documentation Coverage (push) Successful in 3s
Standards Compliance / Accessibility Check (push) Successful in 3s
Standards Compliance / Performance Metrics (push) Successful in 3s
Standards Compliance / Enterprise Readiness Check (push) Successful in 3s
Standards Compliance / Repository Health Check (push) Successful in 4s
Standards Compliance / Terraform Configuration Validation (push) Successful in 6s
CodeQL Security Scanning / Security Scan Summary (push) Successful in 1s
Standards Compliance / Compliance Summary (push) Successful in 1s
Repo Health / Access control (push) Successful in 1s
Auto-Update SHA Hash / Update SHA-256 Hash in updates.xml (release) Successful in 4s
Repo Health / Release configuration (push) Failing after 3s
Repo Health / Scripts governance (push) Successful in 3s
Repo Health / Repository health (push) Failing after 3s
All files renamed from mokocassiopeia to mokoonyx. Update server points to MokoOnyx repo. Bridge migration removed (clean standalone template). Version reset to 01.00.00. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
166 lines
4.8 KiB
PHP
166 lines
4.8 KiB
PHP
<?php
|
|
/**
|
|
* Copyright (C) 2025 Moko Consulting <hello@mokoconsulting.tech>
|
|
*
|
|
* This file is part of a Moko Consulting project.
|
|
*
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
|
*/
|
|
|
|
/**
|
|
* CSS/JS minifier — generates .min files from source when dev mode is off,
|
|
* deletes them when dev mode is on.
|
|
*/
|
|
|
|
defined('_JEXEC') or die;
|
|
|
|
class MokoMinifyHelper
|
|
{
|
|
/**
|
|
* Files to minify: source path relative to template media root.
|
|
* The .min variant is derived automatically (template.css → template.min.css).
|
|
*/
|
|
private const CSS_FILES = [
|
|
'css/template.css',
|
|
'css/offline.css',
|
|
'css/editor.css',
|
|
'css/a11y-high-contrast.css',
|
|
'css/theme/light.standard.css',
|
|
'css/theme/dark.standard.css',
|
|
'css/theme/light.custom.css',
|
|
'css/theme/dark.custom.css',
|
|
];
|
|
|
|
private const JS_FILES = [
|
|
'js/template.js',
|
|
];
|
|
|
|
/**
|
|
* When dev mode is ON: delete all .min files.
|
|
* When dev mode is OFF: regenerate .min files if source is newer.
|
|
*
|
|
* @param string $mediaRoot Absolute path to the template media directory.
|
|
* @param bool $devMode Whether development mode is enabled.
|
|
*/
|
|
public static function sync(string $mediaRoot, bool $devMode): void
|
|
{
|
|
$mediaRoot = rtrim($mediaRoot, '/\\');
|
|
|
|
foreach (self::CSS_FILES as $relPath) {
|
|
$source = $mediaRoot . '/' . $relPath;
|
|
$min = self::minPath($source);
|
|
|
|
if ($devMode) {
|
|
self::deleteIfExists($min);
|
|
} else {
|
|
self::buildIfStale($source, $min, 'css');
|
|
}
|
|
}
|
|
|
|
foreach (self::JS_FILES as $relPath) {
|
|
$source = $mediaRoot . '/' . $relPath;
|
|
$min = self::minPath($source);
|
|
|
|
if ($devMode) {
|
|
self::deleteIfExists($min);
|
|
} else {
|
|
self::buildIfStale($source, $min, 'js');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Derive the .min path from a source path.
|
|
* template.css → template.min.css
|
|
*/
|
|
private static function minPath(string $path): string
|
|
{
|
|
$info = pathinfo($path);
|
|
return $info['dirname'] . '/' . $info['filename'] . '.min.' . $info['extension'];
|
|
}
|
|
|
|
/**
|
|
* Delete a file if it exists.
|
|
*/
|
|
private static function deleteIfExists(string $path): void
|
|
{
|
|
if (is_file($path)) {
|
|
@unlink($path);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Build the minified file if the source is newer or the min file is missing.
|
|
*/
|
|
private static function buildIfStale(string $source, string $min, string $type): void
|
|
{
|
|
if (!is_file($source)) {
|
|
return;
|
|
}
|
|
|
|
// Skip if min file exists and is newer than source
|
|
if (is_file($min) && filemtime($min) >= filemtime($source)) {
|
|
return;
|
|
}
|
|
|
|
$content = file_get_contents($source);
|
|
if ($content === false) {
|
|
return;
|
|
}
|
|
|
|
$minified = ($type === 'css')
|
|
? self::minifyCss($content)
|
|
: self::minifyJs($content);
|
|
|
|
file_put_contents($min, $minified);
|
|
}
|
|
|
|
/**
|
|
* Minify CSS by stripping comments, excess whitespace, and unnecessary characters.
|
|
*/
|
|
private static function minifyCss(string $css): string
|
|
{
|
|
// Remove comments (but keep IE hacks like /*\*/)
|
|
$css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
|
|
|
|
// Remove whitespace around { } : ; , > + ~
|
|
$css = preg_replace('/\s*([{}:;,>+~])\s*/', '$1', $css);
|
|
|
|
// Remove remaining newlines and tabs
|
|
$css = preg_replace('/\s+/', ' ', $css);
|
|
|
|
// Remove spaces around selectors
|
|
$css = str_replace(['{ ', ' {', '; ', ' ;'], ['{', '{', ';', ';'], $css);
|
|
|
|
// Remove trailing semicolons before closing braces
|
|
$css = str_replace(';}', '}', $css);
|
|
|
|
// Remove leading/trailing whitespace
|
|
return trim($css);
|
|
}
|
|
|
|
/**
|
|
* Minify JS by stripping single-line comments, multi-line comments,
|
|
* and collapsing whitespace. Preserves string literals.
|
|
*/
|
|
private static function minifyJs(string $js): string
|
|
{
|
|
// Remove multi-line comments
|
|
$js = preg_replace('!/\*.*?\*/!s', '', $js);
|
|
|
|
// Remove single-line comments (but not URLs like http://)
|
|
$js = preg_replace('!(?<=^|[\s;{}()\[\]])//[^\n]*!m', '', $js);
|
|
|
|
// Collapse whitespace
|
|
$js = preg_replace('/\s+/', ' ', $js);
|
|
|
|
// Remove spaces around operators and punctuation
|
|
$js = preg_replace('/\s*([{}();,=+\-*\/<>!&|?:])\s*/', '$1', $js);
|
|
|
|
// Restore necessary spaces (after keywords)
|
|
$js = preg_replace('/(var|let|const|return|typeof|instanceof|new|delete|throw|case|in|of)([^\s;})><=!&|?:,])/', '$1 $2', $js);
|
|
|
|
return trim($js);
|
|
}
|
|
}
|