Skip to content

Commit 5279118

Browse files
committed
Add purge.json configuration for CSS optimization and implement hybrid testing architecture with Pest
1 parent bcbd7e5 commit 5279118

10 files changed

Lines changed: 1939 additions & 22 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ cp -R {path to extension}/pub/static/* pub/static/
1919
To compile SCSS files in React-Luma, navigate to the module directory (vendor/genaker/magento-reactjs) and run node css-compile.js. This script automatically finds all .scss files in pub/static/, compiles them to minified CSS using Sass and PostCSS with autoprefixer and cssnano, and outputs detailed statistics including file sizes and selector counts. The compiled .min.css files are saved in the same directory, ready for production use. Ensure Node.js is installed and run npm install first to install dependencies (sass, postcss, autoprefixer, cssnano, glob).
2020

2121
## CSS Purge Tool
22-
The project includes a powerful CSS purging tool that removes unused CSS selectors to optimize file sizes and improve performance. The tool uses PurgeCSS with support for URL fetching, local content scanning, and advanced configuration options including ignore patterns and blocklists. To use the CSS purge tool, navigate to `vendor/genaker/magento-reactjs/` and run `node css-purge.js --css path/to/your/file.css`. The tool supports file-specific configurations via `purge.json`, can fetch content from URLs using `--url` parameters, scan local files with `--path` parameters, and apply custom configurations with `--config`. It automatically removes development classes (`.debug-*`, `.test-*`) and deprecated code (`.deprecated-*`, `.old-*`) while preserving critical selectors through safelists. For detailed usage instructions and examples, see the comprehensive documentation in [`vendor/genaker/magento-reactjs/PURGE_README.md`](https://github.com/Genaker/reactmagento2/blob/react-17/PURGE_README.md).
22+
The project includes a powerful CSS purging tool that removes unused CSS selectors to optimize file sizes and improve performance. The tool uses PurgeCSS with support for URL fetching, local content scanning, and advanced configuration options including ignore patterns and blocklists. To use the CSS purge tool, navigate to `vendor/genaker/magento-reactjs/` and run `node css-purge.js --css path/to/your/file.css`. The tool supports file-specific configurations via `purge.json`, can fetch content from URLs using `--url` parameters, scan local files with `--path` parameters, and apply custom configurations with `--config`. It automatically removes development classes (`.debug-*`, `.test-*`) and deprecated code (`.deprecated-*`, `.old-*`) while preserving critical selectors through safelists. For detailed usage instructions and examples, see the comprehensive documentation in `vendor/genaker/magento-reactjs/PURGE_README.md`.
2323

2424
# Video overview of the React-Luma
2525

ReactInjectPlugin.php

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ public function __construct(
7878
*/
7979
protected function renderAssetHtml(\Magento\Framework\View\Asset\PropertyGroup $group)
8080
{
81-
@header('x-built-with: Ract-Luma', false);
81+
@header('x-built-with: React-Luma', false);
8282
$startTime = microtime(true);
8383

8484
$this->objectManager = ObjectManager::getInstance();
@@ -89,7 +89,7 @@ protected function renderAssetHtml(\Magento\Framework\View\Asset\PropertyGroup $
8989
// Process assets
9090
$assets = $this->processMerge($group->getAll(), $group);
9191
$attributes = $this->getGroupAttributes($group);
92-
$type = $group->getProperties()['content_type'];
92+
$type = $group->getProperties()['content_type'] ?? 'css';
9393

9494
// Initialize asset variables
9595
$this->initializeAssetVariables();
@@ -156,11 +156,11 @@ private function getRequestContext(): array
156156
$area = $this->state->getAreaCode();
157157
$request = $this->objectManager->get(\Magento\Framework\App\Request\Http::class);
158158
$actionName = $request->getFullActionName();
159-
$requestURL = $_SERVER['REQUEST_URI'];
159+
$requestURL = $_SERVER['REQUEST_URI'] ?? '';
160160

161161
@header("Action-Name: $actionName");
162162

163-
$removeProtection = boolval(boolval(strpos($requestURL, 'checkout')) || boolval(strpos($requestURL, 'customer')) || $area === 'adminhtml');
163+
$removeProtection = boolval(strpos($requestURL, 'checkout') !== false || strpos($requestURL, 'customer') !== false || $area === 'adminhtml');
164164
@header("React-Protection: $removeProtection");
165165

166166
$removeController = in_array($actionName, $this->actionFilter);
@@ -332,13 +332,15 @@ private function processJavaScriptOptimization(array $assets, array $requestCont
332332
private function processReactVueAssets(array $assets, array $requestContext): array
333333
{
334334
foreach ($assets as $key => $asset) {
335-
if (strpos($asset->getUrl(), 'js/react')) {
335+
$url = $asset->getUrl();
336+
if (strpos($url, 'js/react')) {
336337
unset($assets[$key]);
337338
if ($this->configuration['reactEnabled']) {
338339
array_unshift($assets, $asset);
339340
}
341+
continue; // Skip vue check if already processed as react
340342
}
341-
if (strpos($asset->getUrl(), 'vue')) {
343+
if (strpos($url, 'vue')) {
342344
unset($assets[$key]);
343345
if ($this->configuration['vueEnabled']) {
344346
array_unshift($assets, $asset);
@@ -358,9 +360,13 @@ private function processRequireJSAssets(array $assets, array $requestContext): a
358360
if (strpos($asset->getUrl(), 'require')) {
359361
if ($this->configuration['removeAdobeJSJunk']) {
360362
unset($assets[$key]);
361-
}
362-
363-
if (!$this->configuration['removeAdobeJSJunk'] || !in_array($requestContext['actionName'], $this->actionFilter)) {
363+
// If removing junk AND action is in filter, don't add it back
364+
if (!in_array($requestContext['actionName'], $this->actionFilter)) {
365+
array_unshift($assets, $asset);
366+
}
367+
} else {
368+
// If not removing junk, ensure it's at the front
369+
unset($assets[$key]);
364370
array_unshift($assets, $asset);
365371
}
366372
}
@@ -374,17 +380,24 @@ private function processRequireJSAssets(array $assets, array $requestContext): a
374380
*/
375381
private function reorderAssets(array $assets, array $requestContext): array
376382
{
377-
// Execute one more time to make scripts the same order
378-
foreach ($assets as $key => $asset) {
379-
if (strpos($asset->getUrl(), 'require') && $this->configuration['removeAdobeJSJunk']) {
380-
unset($assets[$key]);
381-
array_unshift($assets, $asset);
383+
// Reorder RequireJS assets if they still exist (not removed by processRequireJSAssets)
384+
if ($this->configuration['removeAdobeJSJunk']) {
385+
foreach ($assets as $key => $asset) {
386+
$url = $asset->getUrl();
387+
if (strpos($url, 'require') && in_array($requestContext['actionName'], $this->actionFilter)) {
388+
// RequireJS should be removed for filtered actions, skip it
389+
continue;
390+
}
391+
if (strpos($url, 'require')) {
392+
unset($assets[$key]);
393+
array_unshift($assets, $asset);
394+
}
382395
}
383-
}
384396

385-
if ($this->configuration['removeAdobeJSJunk']) {
397+
// Reorder React/Vue assets
386398
foreach ($assets as $key => $asset) {
387-
if (strpos($asset->getUrl(), 'js/react') || strpos($asset->getUrl(), 'vue')) {
399+
$url = $asset->getUrl();
400+
if (strpos($url, 'js/react') || strpos($url, 'vue')) {
388401
unset($assets[$key]);
389402
array_unshift($assets, $asset);
390403
}
@@ -461,10 +474,12 @@ private function addProductCSSLinks(string $result, array $pageTypes): string
461474
} else {
462475
$result = '<link rel="stylesheet" type="text/css" media="all" href="' . $this->assetVariables['assetProductOptimized'] . '" />' . "\n" . $result;
463476
}
464-
} else if (!$this->assetVariables['assetProductOptimized'] && $pageTypes['isProduct']) {
477+
} elseif (!$this->assetVariables['assetProductOptimized'] && $pageTypes['isProduct']) {
465478
if ($this->assetVariables['optimisedProductCSSFileCriticalPath'] && $this->checkFile($this->assetVariables['optimisedProductCSSFileCriticalPath'])) {
466479
$result = '<link rel="stylesheet" type="text/css" media="all" href="' . $this->assetVariables['optimisedProductCSSFileCriticalUrl'] . '" />' . "\n" . $result;
467-
$result = '<link rel="stylesheet" media="print" onload="this.onload=null;this.media=\'all\';" href="' . $this->assetVariables['assetNotOptimisedMobile'] . '" />' . "\n" . $result;
480+
if (!empty($this->assetVariables['assetNotOptimisedMobile'])) {
481+
$result = '<link rel="stylesheet" media="print" onload="this.onload=null;this.media=\'all\';" href="' . $this->assetVariables['assetNotOptimisedMobile'] . '" />' . "\n" . $result;
482+
}
468483
}
469484
}
470485

@@ -483,10 +498,12 @@ private function addCategoryCSSLinks(string $result, array $pageTypes): string
483498
} else {
484499
$result = '<link rel="stylesheet" type="text/css" media="all" href="' . $this->assetVariables['assetCategoryOptimized'] . '" />' . "\n" . $result;
485500
}
486-
} else if (!$this->assetVariables['assetCategoryOptimized'] && $pageTypes['isCategory']) {
501+
} elseif (!$this->assetVariables['assetCategoryOptimized'] && $pageTypes['isCategory']) {
487502
if ($this->assetVariables['optimisedCategoryCSSFileCriticalPath'] && $this->checkFile($this->assetVariables['optimisedCategoryCSSFileCriticalPath'])) {
488503
$result = '<link rel="stylesheet" type="text/css" media="all" href="' . $this->assetVariables['optimisedCategoryCSSFileCriticalUrl'] . '" />' . "\n" . $result;
489-
$result = '<link rel="stylesheet" media="print" onload="this.onload=null;this.media=\'all\';" href="' . $this->assetVariables['assetNotOptimisedLarge'] . '" />' . "\n" . $result;
504+
if (!empty($this->assetVariables['assetNotOptimisedLarge'])) {
505+
$result = '<link rel="stylesheet" media="print" onload="this.onload=null;this.media=\'all\';" href="' . $this->assetVariables['assetNotOptimisedLarge'] . '" />' . "\n" . $result;
506+
}
490507
}
491508
}
492509

purge.json

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
{
2+
"styles-m.min.css": {
3+
"content": {
4+
"urls": [
5+
"https://react-luma.cnxt.link",
6+
"https://react-luma.cnxt.link/men/bottoms-men/pants-men.html"
7+
],
8+
"paths": [
9+
"content/react_luma_cnxt_link_1.html",
10+
"content/react_luma_cnxt_link_2.html"
11+
]
12+
},
13+
"options": {
14+
"safelist": [
15+
"html",
16+
"body",
17+
"head",
18+
"script",
19+
"style"
20+
],
21+
"ignore": [
22+
".debug-*",
23+
".test-*",
24+
".temp-*",
25+
".unused-*"
26+
],
27+
"blocklist": [
28+
".deprecated-*",
29+
".old-*",
30+
".legacy-*",
31+
".vendor-prefix-*"
32+
]
33+
}
34+
},
35+
"styles-l.min.css": {
36+
"content": {
37+
"urls": [
38+
"https://react-luma.cnxt.link"
39+
],
40+
"paths": [
41+
"content/react_luma_cnxt_link_1.html"
42+
]
43+
},
44+
"options": {
45+
"safelist": [
46+
"html",
47+
"body",
48+
"head",
49+
"script",
50+
"style"
51+
],
52+
"ignore": [
53+
".debug-*",
54+
".test-*"
55+
],
56+
"blocklist": [
57+
".deprecated-*",
58+
".old-*"
59+
]
60+
}
61+
},
62+
"product-styles-m.min.css": {
63+
"content": {
64+
"urls": [
65+
"https://react-luma.cnxt.link",
66+
"https://react-luma.cnxt.link/men/bottoms-men/pants-men.html"
67+
],
68+
"paths": [
69+
"content/react_luma_cnxt_link_1.html"
70+
]
71+
},
72+
"options": {
73+
"safelist": [
74+
"html",
75+
"body",
76+
"head",
77+
"script",
78+
"style",
79+
"product-item",
80+
"product-image"
81+
],
82+
"ignore": [
83+
".debug-*",
84+
".test-*",
85+
".temp-*",
86+
".unused-*",
87+
".product-debug-*"
88+
],
89+
"blocklist": [
90+
".deprecated-*",
91+
".old-*",
92+
".legacy-*",
93+
".vendor-prefix-*",
94+
".product-old-*"
95+
]
96+
}
97+
},
98+
"category-styles-m.min.css": {
99+
"content": {
100+
"urls": [
101+
"https://react-luma.cnxt.link/men/bottoms-men/pants-men.html"
102+
],
103+
"paths": [
104+
"content/react_luma_cnxt_link_2.html"
105+
]
106+
},
107+
"options": {
108+
"safelist": [
109+
"html",
110+
"body",
111+
"head",
112+
"script",
113+
"style",
114+
"category-page",
115+
"product-grid"
116+
],
117+
"ignore": [
118+
".debug-*",
119+
".test-*",
120+
".temp-*",
121+
".unused-*",
122+
".category-debug-*"
123+
],
124+
"blocklist": [
125+
".deprecated-*",
126+
".old-*",
127+
".legacy-*",
128+
".vendor-prefix-*",
129+
".category-old-*"
130+
]
131+
}
132+
}
133+
}

tests/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/vendor
2+
/composer.lock

tests/Pest.php

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
use Pest\TestSuite;
4+
5+
/*
6+
|--------------------------------------------------------------------------
7+
| Test Case
8+
|--------------------------------------------------------------------------
9+
|
10+
| The closure you provide to your test functions is always bound to a specific PHPUnit test
11+
| case class. By default, that class is "PHPUnit\Framework\TestCase". Of course, you may
12+
| need to change it using the "uses()" function to bind a different classes or traits.
13+
|
14+
*/
15+
16+
uses()->group('unit')->in('Unit');
17+
18+
/*
19+
|--------------------------------------------------------------------------
20+
| Expectations
21+
|--------------------------------------------------------------------------
22+
|
23+
| When you're writing tests, you often need to check that values meet certain conditions. The
24+
| "expect()" function gives you access to a set of "expectations" methods that you can use
25+
| to assert different things. Of course, you may extend the Expectation API at any time.
26+
|
27+
*/
28+
29+
expect()->extend('toBeOne', function () {
30+
return $this->toBe(1);
31+
});
32+
33+
/*
34+
|--------------------------------------------------------------------------
35+
| Functions
36+
|--------------------------------------------------------------------------
37+
|
38+
| While Pest is very powerful out-of-the-box, you may have some testing code specific to your
39+
| project that you don't want to repeat in every file. Here you can also expose helpers as
40+
| global functions to help you to reduce the amount of code you need to write.
41+
|
42+
*/

0 commit comments

Comments
 (0)