From feddb35a52a47867e4e3cc52d9568ce36dbd0ddb Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Fri, 4 Nov 2022 10:44:55 +0200 Subject: [PATCH 01/18] delete cache clearing command during generation flow --- src/Console/Command/GenerateCommand.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index 9ee08c7..c4a18e2 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -96,10 +96,13 @@ protected function execute(InputInterface $input, OutputInterface $output) try { $this->state->setAreaCode(\Magento\Framework\App\Area::AREA_ADMINHTML); - $this->cacheManager->flush($this->cacheManager->getAvailableTypes()); + // TODO: decide whether cache flushing is really required. temporally commented. + //$this->cacheManager->flush($this->cacheManager->getAvailableTypes()); $this->criticalCssService->test($this->config->getCriticalBinary()); + $consoleHandler = $this->consoleHandlerFactory->create(['output' => $output]); + $logger = $this->objectManager->create('M2Boilerplate\CriticalCss\Logger\Console', ['handlers' => ['console' => $consoleHandler]]); $output->writeln('Generating Critical CSS'); From 14a3cfabc930c7e15e9600880ebb2412d2b5e998 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Fri, 4 Nov 2022 10:47:02 +0200 Subject: [PATCH 02/18] skip Magneto Critical CSS flag during CSS file generation flow --- src/Plugin/AsyncCssPlugin.php | 55 +++++++++++++++++++++++++++++++++++ src/etc/di.xml | 10 +++++-- 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 src/Plugin/AsyncCssPlugin.php diff --git a/src/Plugin/AsyncCssPlugin.php b/src/Plugin/AsyncCssPlugin.php new file mode 100644 index 0000000..095dedc --- /dev/null +++ b/src/Plugin/AsyncCssPlugin.php @@ -0,0 +1,55 @@ +scopeConfig = $scopeConfig; + $this->request = $request; + } + /** + * Returns information whether css critical path is enabled + * css critical is disabled for requests generated by critical npm headless browser + * @return bool + */ + private function isCssCriticalEnabled(): bool + { + return $this->isCriticalCssGenerateRequest() === false + && $this->scopeConfig->isSetFlag( + self::XML_PATH_USE_CSS_CRITICAL_PATH, + ScopeInterface::SCOPE_STORE + ); + } + + /** + * indicates whether request is generated by critical npm headless browser as part of critical css generation process + * @return mixed + */ + private function isCriticalCssGenerateRequest() { + return $this->request->getParam(self::CRITICAL_CSS_GENERATE_QUERY_PARAM, false); + } +} diff --git a/src/etc/di.xml b/src/etc/di.xml index 9d195f2..6bf0f68 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -11,11 +11,11 @@ M2Boilerplate\CriticalCss\Provider\DefaultProvider M2Boilerplate\CriticalCss\Provider\CmsPageProvider - M2Boilerplate\CriticalCss\Provider\CustomerProvider + @@ -44,4 +44,8 @@ M2Boilerplate\CriticalCss\Logger\File - \ No newline at end of file + + + + + From ab37186c66736248e5316f2ac677b5e2d933389f Mon Sep 17 00:00:00 2001 From: Denis_Z Date: Fri, 4 Nov 2022 14:43:23 +0400 Subject: [PATCH 03/18] M2CRIT-1: Skip Magento native Critical CSS flow * Updated: logic of base Plugin; * Implemented: validation on UserAgent value; --- src/Plugin/AsyncCssPlugin.php | 57 ++++++++++++++--------------------- src/etc/di.xml | 9 ++++-- src/etc/module.xml | 8 +++-- 3 files changed, 36 insertions(+), 38 deletions(-) diff --git a/src/Plugin/AsyncCssPlugin.php b/src/Plugin/AsyncCssPlugin.php index 095dedc..52d238b 100644 --- a/src/Plugin/AsyncCssPlugin.php +++ b/src/Plugin/AsyncCssPlugin.php @@ -3,53 +3,42 @@ namespace M2Boilerplate\CriticalCss\Plugin; use Magento\Framework\App\Config\ScopeConfigInterface; -use Magento\Store\Model\ScopeInterface; +use Magento\Framework\App\ResponseInterface; +use Magento\Framework\HTTP\Header; +use Magento\Framework\View\Result\Layout; class AsyncCssPlugin extends \Magento\Theme\Controller\Result\AsyncCssPlugin { + private Header $httpHeader; - private const XML_PATH_USE_CSS_CRITICAL_PATH = 'dev/css/use_css_critical_path'; - - private const CRITICAL_CSS_GENERATE_QUERY_PARAM = 'critical_css_generate'; - /** - * @var ScopeConfigInterface - */ - private $scopeConfig; - - /** - * @var \Magento\Framework\App\RequestInterface - */ - private $request; - /** - * @param ScopeConfigInterface $scopeConfig - */ public function __construct( ScopeConfigInterface $scopeConfig, - \Magento\Framework\App\RequestInterface $request - ) - { - $this->scopeConfig = $scopeConfig; - $this->request = $request; + Header $httpHeader + ) { + parent::__construct($scopeConfig); + $this->httpHeader = $httpHeader; } + /** - * Returns information whether css critical path is enabled - * css critical is disabled for requests generated by critical npm headless browser - * @return bool + * @inheritDoc */ - private function isCssCriticalEnabled(): bool + public function afterRenderResult(Layout $subject, Layout $result, ResponseInterface $httpResponse) { - return $this->isCriticalCssGenerateRequest() === false - && $this->scopeConfig->isSetFlag( - self::XML_PATH_USE_CSS_CRITICAL_PATH, - ScopeInterface::SCOPE_STORE - ); + if ($this->canBeProcessed()) { + return parent::afterRenderResult($subject, $result, $httpResponse); + } + + return $result; } /** - * indicates whether request is generated by critical npm headless browser as part of critical css generation process - * @return mixed + * @return bool */ - private function isCriticalCssGenerateRequest() { - return $this->request->getParam(self::CRITICAL_CSS_GENERATE_QUERY_PARAM, false); + private function canBeProcessed(): bool + { + // NOTE: validate Request it MUST NOT be initiated by NPM CRITICAL-CSS + // check on user-agent value + + return $this->httpHeader->getHttpUserAgent() !== 'got (https://github.com/sindresorhus/got)'; } } diff --git a/src/etc/di.xml b/src/etc/di.xml index 6bf0f68..6c0d506 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -46,6 +46,11 @@ - - + + + + diff --git a/src/etc/module.xml b/src/etc/module.xml index 5581f13..d96095c 100644 --- a/src/etc/module.xml +++ b/src/etc/module.xml @@ -1,4 +1,8 @@ - - \ No newline at end of file + + + + + + From 97ec6a6d27cff4da903bc8e7e57f042ed327748d Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Sun, 6 Nov 2022 18:25:54 +0200 Subject: [PATCH 04/18] restore providers --- src/etc/di.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/etc/di.xml b/src/etc/di.xml index 6c0d506..927927a 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -11,11 +11,11 @@ M2Boilerplate\CriticalCss\Provider\DefaultProvider M2Boilerplate\CriticalCss\Provider\CmsPageProvider - + M2Boilerplate\CriticalCss\Provider\CategoryProvider From b0ba43cb3be77f458fbb6d52a188cb4822633da6 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Sun, 6 Nov 2022 18:26:39 +0200 Subject: [PATCH 05/18] deprecate caching flushing during generate command --- src/Console/Command/GenerateCommand.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index c4a18e2..48c97a0 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -113,7 +113,8 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln('Generating Critical CSS for ' . count($processes) . ' URLs...'); $processManager->executeProcesses($processes, true); - $this->cacheManager->flush($this->cacheManager->getAvailableTypes()); + // TODO: decide whether cache flushing is really required. temporally commented. + // $this->cacheManager->flush($this->cacheManager->getAvailableTypes()); } catch (\Throwable $e) { throw $e; From 43c321100d3abbeaf1f1acfdf6ab320d100dc3e6 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Sun, 6 Nov 2022 19:31:38 +0200 Subject: [PATCH 06/18] move asyncCssLoad to frontend/di.xml --- src/etc/di.xml | 11 +---------- src/etc/frontend/di.xml | 12 +++++++++++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/etc/di.xml b/src/etc/di.xml index 927927a..1691666 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -11,7 +11,7 @@ M2Boilerplate\CriticalCss\Provider\DefaultProvider M2Boilerplate\CriticalCss\Provider\CmsPageProvider - M2Boilerplate\CriticalCss\Provider\CustomerProvider + M2Boilerplate\CriticalCss\Provider\CustomerProvider M2Boilerplate\CriticalCss\Provider\ContactProvider M2Boilerplate\CriticalCss\Provider\CatalogSearchProvider M2Boilerplate\CriticalCss\Provider\ProductProvider @@ -44,13 +44,4 @@ M2Boilerplate\CriticalCss\Logger\File - - - - - - diff --git a/src/etc/frontend/di.xml b/src/etc/frontend/di.xml index c413f92..5f8b056 100644 --- a/src/etc/frontend/di.xml +++ b/src/etc/frontend/di.xml @@ -2,4 +2,14 @@ - \ No newline at end of file + + + + + + + + From 317fd362ea41f1b2261a1d12b5597d9acfdcb02c Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Sun, 6 Nov 2022 20:25:25 +0200 Subject: [PATCH 07/18] add ingore-rule option to critical cli to ignore font-face --- src/Service/CriticalCss.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Service/CriticalCss.php b/src/Service/CriticalCss.php index c50de56..64ac3fa 100644 --- a/src/Service/CriticalCss.php +++ b/src/Service/CriticalCss.php @@ -44,8 +44,9 @@ public function createCriticalCssProcess( $command[] = '--strict'; $command[] = '--no-request-https.rejectUnauthorized'; - $command[] = '--ignore-rule'; - $command[] = '[data-role=main-css-loader]'; + + $command[] = '--ignore-atrule'; + $command[] = '@font-face'; /** @var Process $process */ $process = $this->processFactory->create(['command' => $command, 'commandline' => $command]); From b993057a64e9210e51a12a795dc5f9d47a83161c Mon Sep 17 00:00:00 2001 From: Denis_Z Date: Mon, 7 Nov 2022 10:53:56 +0400 Subject: [PATCH 08/18] M2CRIT-2: add random query string param * Implemented: workaround to provide processing url with random query-string param-value; --- src/Service/ProcessManager.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Service/ProcessManager.php b/src/Service/ProcessManager.php index f1fa3e1..7ddea84 100644 --- a/src/Service/ProcessManager.php +++ b/src/Service/ProcessManager.php @@ -145,6 +145,13 @@ public function createProcessesForProvider(ProviderInterface $provider, StoreInt $processList = []; $urls = $provider->getUrls($store); foreach ($urls as $identifier => $url) { + // NOTE: start: SR WORKAROUND + // to add random query string to generated URLs to get non cached page content + $qParamConcatChar = mb_strpos($url, '?') === false ? '?' : '&'; + $url .= $qParamConcatChar . 'm2bp_t=' . time(); + // end: SR WORKAROUND + + $this->logger->info(sprintf('[%s:%s|%s] - %s', $store->getCode(), $provider->getName(), $identifier, $url)); $process = $this->criticalCssService->createCriticalCssProcess( $url, From 0da31e406aaf757e588c364207932e2878255294 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Mon, 7 Nov 2022 22:28:16 +0200 Subject: [PATCH 09/18] add penthouse force include CSS selectors option --- src/Config/Config.php | 13 +++++++++- src/Console/Command/GenerateCommand.php | 17 ++++++++++++- src/Service/CriticalCss.php | 10 ++++++++ src/Service/ProcessManager.php | 1 + src/etc/adminhtml/system.xml | 34 +++++++++++++++---------- 5 files changed, 60 insertions(+), 15 deletions(-) diff --git a/src/Config/Config.php b/src/Config/Config.php index a7864d7..fd230bd 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -14,6 +14,7 @@ class Config const CONFIG_PATH_USERNAME = 'dev/css/critical_css_username'; const CONFIG_PATH_PASSWORD = 'dev/css/critical_css_password'; const CONFIG_PATH_DIMENSIONS = 'dev/css/critical_css_dimensions'; + const CONFIG_PATH_FORCE_INCLUDE_CSS_SELECTORS = 'dev/css/critical_css_force_include_css_selectors'; /** * @var ScopeConfigInterface @@ -38,6 +39,16 @@ public function isEnabled(): bool return (bool) $this->scopeConfig->isSetFlag(self::CONFIG_PATH_ENABLED); } + public function getForceIncludeCssSelectors(): array + { + $cssSelectors = $this->scopeConfig->getValue(self::CONFIG_PATH_FORCE_INCLUDE_CSS_SELECTORS); + $cssSelectors = explode(',', $cssSelectors); + $cssSelectors = array_map('trim', $cssSelectors); + $cssSelectors = array_filter($cssSelectors); + + return $cssSelectors; + } + public function getDimensions(): array { $dimensions = $this->scopeConfig->getValue(self::CONFIG_PATH_DIMENSIONS); @@ -78,4 +89,4 @@ public function getCriticalBinary(): string { return $this->scopeConfig->getValue(self::CONFIG_PATH_CRITICAL_BINARY); } -} \ No newline at end of file +} diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index 48c97a0..30b5058 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -104,13 +104,28 @@ protected function execute(InputInterface $input, OutputInterface $output) $consoleHandler = $this->consoleHandlerFactory->create(['output' => $output]); $logger = $this->objectManager->create('M2Boilerplate\CriticalCss\Logger\Console', ['handlers' => ['console' => $consoleHandler]]); - $output->writeln('Generating Critical CSS'); /** @var ProcessManager $processManager */ $processManager = $this->processManagerFactory->create(['logger' => $logger]); + + + $output->writeln('\'Use CSS critical path\' status ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . ''); + $output->writeln("-----------------------------------------"); + $output->writeln('Critical Command Configured Options'); + $output->writeln("-----------------------------------------"); + $output->writeln('Screen Dimensions: ' . implode('', $this->config->getDimensions()) . ''); + $output->writeln('Force Include Css Selectors: ' . implode('', $this->config->getForceIncludeCssSelectors()) . ''); + + $output->writeln('HTTP Auth Username: ' . $this->config->getUsername() . ''); + $output->writeln('HTTP Auth Password: ' . $this->config->getPassword() . ''); + + $output->writeln("-----------------------------------------"); $output->writeln('Gathering URLs...'); + $output->writeln("-----------------------------------------"); $processes = $processManager->createProcesses(); + $output->writeln("-----------------------------------------"); $output->writeln('Generating Critical CSS for ' . count($processes) . ' URLs...'); + $output->writeln("-----------------------------------------"); $processManager->executeProcesses($processes, true); // TODO: decide whether cache flushing is really required. temporally commented. diff --git a/src/Service/CriticalCss.php b/src/Service/CriticalCss.php index 64ac3fa..931c1c0 100644 --- a/src/Service/CriticalCss.php +++ b/src/Service/CriticalCss.php @@ -23,6 +23,7 @@ public function createCriticalCssProcess( string $url, array $dimensions, string $criticalBinary = 'critical', + array $forceIncludeCssSelectors, ?string $username = null, ?string $password = null ) { @@ -30,6 +31,12 @@ public function createCriticalCssProcess( $criticalBinary, $url ]; + + foreach ($forceIncludeCssSelectors as $selector) { + $command[] = '--penthouse-forceInclude'; + $command[] = $selector; + } + foreach ($dimensions as $dimension) { $command[] = '--dimensions'; $command[] = $dimension; @@ -48,6 +55,9 @@ public function createCriticalCssProcess( $command[] = '--ignore-atrule'; $command[] = '@font-face'; + //$command[] = '--penthouse-blockJSRequests'; + //$command[] = 'true'; + /** @var Process $process */ $process = $this->processFactory->create(['command' => $command, 'commandline' => $command]); diff --git a/src/Service/ProcessManager.php b/src/Service/ProcessManager.php index f1fa3e1..8a479b8 100644 --- a/src/Service/ProcessManager.php +++ b/src/Service/ProcessManager.php @@ -150,6 +150,7 @@ public function createProcessesForProvider(ProviderInterface $provider, StoreInt $url, $this->config->getDimensions(), $this->config->getCriticalBinary(), + $this->config->getForceIncludeCssSelectors(), $this->config->getUsername(), $this->config->getPassword() ); diff --git a/src/etc/adminhtml/system.xml b/src/etc/adminhtml/system.xml index 0abb77c..cc88d2b 100644 --- a/src/etc/adminhtml/system.xml +++ b/src/etc/adminhtml/system.xml @@ -3,40 +3,48 @@
- - + + 1 required-entry Installation instructions can be found here: https://github.com/addyosmani/critical#install - - + + 1 - required-entry - Comma separated List, e.g.: 375x812,576x1152,768x1024,1024x768,1280x720 + validate-digits required-entry - - + + 1 - validate-digits required-entry - - + + 1 - - + + 1 + required-entry + Comma separated List, e.g.: 375x812,576x1152,768x1024,1024x768,1280x720 + + + + + 1 + + required-entry + Comma separated css selectors to keep in critical css, even if not appearing in critical viewport. Strings or regex (f.e. '.keepMeEvenIfNotSeenInDom', /^\.button/)
From 0afa7b0c3321b4997fac6332b52a52c278d81318 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Tue, 8 Nov 2022 12:06:01 +0200 Subject: [PATCH 10/18] CLI command info output rephrasing --- src/Console/Command/GenerateCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index 30b5058..faec59c 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -109,7 +109,7 @@ protected function execute(InputInterface $input, OutputInterface $output) $processManager = $this->processManagerFactory->create(['logger' => $logger]); - $output->writeln('\'Use CSS critical path\' status ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . ''); + $output->writeln('\'Use CSS critical path\' config is ' . ($this->config->isEnabled() ? 'Enabled' : 'Disabled') . ''); $output->writeln("-----------------------------------------"); $output->writeln('Critical Command Configured Options'); $output->writeln("-----------------------------------------"); From 90ba3f5d2a0fb36d220034cc05df03aa9cee8731 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Tue, 8 Nov 2022 12:13:44 +0200 Subject: [PATCH 11/18] deprecate DefaultProvider --- src/etc/di.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/etc/di.xml b/src/etc/di.xml index 9d195f2..ffde044 100644 --- a/src/etc/di.xml +++ b/src/etc/di.xml @@ -9,7 +9,7 @@ - M2Boilerplate\CriticalCss\Provider\DefaultProvider + M2Boilerplate\CriticalCss\Provider\CmsPageProvider M2Boilerplate\CriticalCss\Provider\CustomerProvider M2Boilerplate\CriticalCss\Provider\ContactProvider @@ -44,4 +44,4 @@ M2Boilerplate\CriticalCss\Logger\File - \ No newline at end of file + From 2fa822aed880d647e65246835f5d824aa8817445 Mon Sep 17 00:00:00 2001 From: Denis_Z Date: Tue, 8 Nov 2022 15:11:27 +0400 Subject: [PATCH 12/18] M2CRIT-6: Skip Critical CSS Magento native flow * Implemented: logic to skip Critical CSS nodes manipulation, in case crit-css style-node does not exist or is empty; * Re-factored: logic of AsyncCssPlugin; --- src/Plugin/AsyncCssPlugin.php | 89 +++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/src/Plugin/AsyncCssPlugin.php b/src/Plugin/AsyncCssPlugin.php index 52d238b..88d8cb2 100644 --- a/src/Plugin/AsyncCssPlugin.php +++ b/src/Plugin/AsyncCssPlugin.php @@ -6,16 +6,23 @@ use Magento\Framework\App\ResponseInterface; use Magento\Framework\HTTP\Header; use Magento\Framework\View\Result\Layout; +use Magento\Store\Model\ScopeInterface; class AsyncCssPlugin extends \Magento\Theme\Controller\Result\AsyncCssPlugin { + private ScopeConfigInterface $scopeConfig; private Header $httpHeader; + /** + * @param ScopeConfigInterface $scopeConfig + * @param Header $httpHeader + */ public function __construct( ScopeConfigInterface $scopeConfig, Header $httpHeader ) { parent::__construct($scopeConfig); + $this->scopeConfig = $scopeConfig; $this->httpHeader = $httpHeader; } @@ -24,7 +31,7 @@ public function __construct( */ public function afterRenderResult(Layout $subject, Layout $result, ResponseInterface $httpResponse) { - if ($this->canBeProcessed()) { + if ($this->canBeProcessed($httpResponse)) { return parent::afterRenderResult($subject, $result, $httpResponse); } @@ -34,11 +41,85 @@ public function afterRenderResult(Layout $subject, Layout $result, ResponseInter /** * @return bool */ - private function canBeProcessed(): bool + private function canBeProcessed(ResponseInterface $httpResponse): bool { - // NOTE: validate Request it MUST NOT be initiated by NPM CRITICAL-CSS + // NOTE: validate Critical Css activity Flag + if (!$this->isCssCriticalEnabled()) { + return false; + } + + // NOTE: validate Request, it MUST NOT be initiated by NPM CRITICAL-CSS // check on user-agent value + if ($this->httpHeader->getHttpUserAgent() === 'got (https://github.com/sindresorhus/got)') { + return false; + } + + // NOTE: validate if CriticalCss node includes not-empty content + $content = (string)$httpResponse->getContent(); + if ($this->isCriticalCssNodeEmpty($content)) { + return false; + } + + return true; + } + + /** + * NOTE: + * @see \Magento\Theme\Controller\Result\AsyncCssPlugin::isCssCriticalEnabled + * + * Returns information whether css critical path is enabled + * + * @return bool + */ + private function isCssCriticalEnabled(): bool + { + return $this->scopeConfig->isSetFlag( + 'dev/css/use_css_critical_path', + ScopeInterface::SCOPE_STORE + ); + } + + /** + * Validates if STYLE CriticalCss-node exists and is NOT empty + * + * @param string $content + * @return bool + */ + private function isCriticalCssNodeEmpty(string $content): bool + { + $styles = ''; + $styleOpen = ''); + + while ($styleOpenPos !== false) { + // NOTE: no need to proceed in case lines on HEAD-node have been processed + if ($styleOpenPos >= $headClosePos) { + break; + } + + $styleClosePos = strpos($content, $styleClose, $styleOpenPos); + $style = substr($content, $styleOpenPos, $styleClosePos - $styleOpenPos + strlen($styleClose)); + + // NOTE: validation of STYLE-node string (tags exactly and node's inner content). + // in case match - fetch the styles and filter the string + if (preg_match('@(.+)@s', $style, $matches)) { + $styles = str_replace( + ["\n","\r\n","\r"], + '', + trim((string)($matches[2] ?? null))); + break; + } + + // NOTE: remove processed style-node from HTML. + $content = str_replace($style, '', $content); + + // NOTE: style-node was cut out, search for the next one at its former position. + $styleOpenPos = strpos($content, $styleOpen, $styleOpenPos); + } - return $this->httpHeader->getHttpUserAgent() !== 'got (https://github.com/sindresorhus/got)'; + return empty($styles); } } From 7e8bb7a62cae8c866252a20526dede511056c55b Mon Sep 17 00:00:00 2001 From: Denis_Z Date: Tue, 8 Nov 2022 20:46:01 +0400 Subject: [PATCH 13/18] M2CRIT-8: add store-id InputOption for cli command * Implemented: logic to get and handle specified Store Ids to limit processing Stores; --- src/Console/Command/GenerateCommand.php | 44 ++++++++++++++++++++++--- src/Service/ProcessManager.php | 14 ++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/src/Console/Command/GenerateCommand.php b/src/Console/Command/GenerateCommand.php index faec59c..866f78e 100644 --- a/src/Console/Command/GenerateCommand.php +++ b/src/Console/Command/GenerateCommand.php @@ -7,17 +7,20 @@ use M2Boilerplate\CriticalCss\Service\CriticalCss; use M2Boilerplate\CriticalCss\Service\ProcessManager; use M2Boilerplate\CriticalCss\Service\ProcessManagerFactory; +use Magento\Framework\App\Cache\Manager; use Magento\Framework\App\Config\Storage\WriterInterface; -use Magento\Framework\ObjectManagerInterface; use Magento\Framework\App\State; +use Magento\Framework\FlagManager; +use Magento\Framework\ObjectManagerInterface; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; -use Magento\Framework\FlagManager; -use Magento\Framework\App\Cache\Manager; class GenerateCommand extends Command { + public const INPUT_OPTION_KEY_STORE_IDS = 'store-id'; + /** * @var ProcessManagerFactory */ @@ -88,6 +91,7 @@ public function __construct( protected function configure() { $this->setName('m2bp:critical-css:generate'); + $this->getDefinition()->addOptions($this->getOptionsList()); parent::configure(); } @@ -122,7 +126,11 @@ protected function execute(InputInterface $input, OutputInterface $output) $output->writeln("-----------------------------------------"); $output->writeln('Gathering URLs...'); $output->writeln("-----------------------------------------"); - $processes = $processManager->createProcesses(); + + $processes = $processManager->createProcesses( + $this->getStoreIds($input) ?: null + ); + $output->writeln("-----------------------------------------"); $output->writeln('Generating Critical CSS for ' . count($processes) . ' URLs...'); $output->writeln("-----------------------------------------"); @@ -136,4 +144,32 @@ protected function execute(InputInterface $input, OutputInterface $output) } return 0; } + + /** + * Returns list of options and arguments for the command + * + * @return mixed + */ + public function getOptionsList() + { + return [ + new InputOption( + self::INPUT_OPTION_KEY_STORE_IDS, + null, + InputOption::VALUE_REQUIRED, + 'Coma-separated list of Magento Store IDs or single value to process specific Store.' + ), + ]; + } + + /** + * @param InputInterface $input + * @return int[] + */ + private function getStoreIds(InputInterface $input): array + { + $ids = $input->getOption(self::INPUT_OPTION_KEY_STORE_IDS) ?: ''; + $ids = explode(',', $ids); + return array_map('intval', array_filter($ids)); + } } diff --git a/src/Service/ProcessManager.php b/src/Service/ProcessManager.php index bf77aff..ff20f3f 100644 --- a/src/Service/ProcessManager.php +++ b/src/Service/ProcessManager.php @@ -2,9 +2,9 @@ namespace M2Boilerplate\CriticalCss\Service; -use M2Boilerplate\CriticalCss\Model\ProcessContextFactory; use M2Boilerplate\CriticalCss\Config\Config; use M2Boilerplate\CriticalCss\Model\ProcessContext; +use M2Boilerplate\CriticalCss\Model\ProcessContextFactory; use M2Boilerplate\CriticalCss\Provider\Container; use M2Boilerplate\CriticalCss\Provider\ProviderInterface; use Magento\Store\Api\Data\StoreInterface; @@ -121,10 +121,20 @@ public function executeProcesses(array $processList, bool $deleteOldFiles = fals } - public function createProcesses(): array + /** + * @param array|null $storeIds + * @return array + */ + public function createProcesses(?array $storeIds = null): array { $processList = []; foreach ($this->storeManager->getStores() as $storeId => $store) { + // NOTE: skip Store in case specific StoreIds to process are provided, + // but current StoreId is not in the List + if ($storeIds !== null && !in_array($storeId, $storeIds, true)) { + continue; + } + // Skip store if store is not active if (!$store->getIsActive()) continue; $this->emulation->startEnvironmentEmulation($storeId,\Magento\Framework\App\Area::AREA_FRONTEND, true); From 0b02dce592702e24d4031b6d11972fa3b8daa3ad Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Wed, 9 Nov 2022 15:57:15 +0200 Subject: [PATCH 14/18] inject only generated css code --- src/Plugin/CriticalCss.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/Plugin/CriticalCss.php b/src/Plugin/CriticalCss.php index e3a8149..3f305bd 100644 --- a/src/Plugin/CriticalCss.php +++ b/src/Plugin/CriticalCss.php @@ -65,9 +65,18 @@ public function __construct( $this->storeManager = $storeManager; } + /** + * @param \Magento\Theme\Block\Html\Header\CriticalCss $subject + * @param $result generated CSS code to be inline injected to page head + * @return string|null + * @throws \Magento\Framework\Exception\FileSystemException + */ public function afterGetCriticalCssData(\Magento\Theme\Block\Html\Header\CriticalCss $subject, $result) { + $result = ''; + $providers = $this->container->getProviders(); + try { $store = $this->storeManager->getStore(); } catch (NoSuchEntityException $e) { @@ -77,9 +86,9 @@ public function afterGetCriticalCssData(\Magento\Theme\Block\Html\Header\Critica foreach ($providers as $provider) { if ($identifier = $provider->getCssIdentifierForRequest($this->request, $this->layout)) { $identifier = $this->identifier->generateIdentifier($provider, $store, $identifier); - $css = $this->storage->getCriticalCss($identifier); - if ($css) { - return $css; + $result = $this->storage->getCriticalCss($identifier); + if ($result) { + break; } } } From 2289c53c64348957398a5e78389ee7b186a5e074 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Wed, 9 Nov 2022 15:58:57 +0200 Subject: [PATCH 15/18] Update composer.json --- composer.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/composer.json b/composer.json index 0b64664..868122d 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,9 @@ { "name": "m2-boilerplate/module-critical-css", "description": "Magento 2 module to automatically generate critical css with the addyosmani/critical npm package", + "type": "magento2-module", + "prefer-stable": true, + "version": "2.0.0", "keywords": [ ], "require": { From 1f60846313c53541f9a4b7a8e54f077eac91d0d5 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Wed, 9 Nov 2022 21:50:39 +0200 Subject: [PATCH 16/18] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 868122d..0a23f77 100644 --- a/composer.json +++ b/composer.json @@ -1,5 +1,5 @@ { - "name": "m2-boilerplate/module-critical-css", + "name": "studioraz/magento2-critical-css", "description": "Magento 2 module to automatically generate critical css with the addyosmani/critical npm package", "type": "magento2-module", "prefer-stable": true, From 3652bd001b3745ed0d030c20736d226ad7209a40 Mon Sep 17 00:00:00 2001 From: Itay Raz Date: Wed, 9 Nov 2022 21:58:03 +0200 Subject: [PATCH 17/18] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 0a23f77..024e366 100644 --- a/composer.json +++ b/composer.json @@ -3,7 +3,7 @@ "description": "Magento 2 module to automatically generate critical css with the addyosmani/critical npm package", "type": "magento2-module", "prefer-stable": true, - "version": "2.0.0", + "version": "2.0.1", "keywords": [ ], "require": { From fcab705a8f49db30b1509828c2ef03b2d700258f Mon Sep 17 00:00:00 2001 From: maxim Date: Mon, 1 Jul 2024 13:35:08 +0300 Subject: [PATCH 18/18] added php 8.x compatibility --- composer.json | 7 +------ src/Service/CriticalCss.php | 2 +- src/Service/ProcessManager.php | 2 +- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 024e366..84e0b20 100644 --- a/composer.json +++ b/composer.json @@ -3,14 +3,9 @@ "description": "Magento 2 module to automatically generate critical css with the addyosmani/critical npm package", "type": "magento2-module", "prefer-stable": true, - "version": "2.0.1", + "version": "2.1.0", "keywords": [ ], - "require": { - "magento/framework": "^102.0|^103.0", - "php": ">=7.1.0" - }, - "type": "magento2-module", "license": [ "MIT" ], diff --git a/src/Service/CriticalCss.php b/src/Service/CriticalCss.php index 931c1c0..e427d37 100644 --- a/src/Service/CriticalCss.php +++ b/src/Service/CriticalCss.php @@ -22,8 +22,8 @@ public function __construct(ProcessFactory $processFactory) public function createCriticalCssProcess( string $url, array $dimensions, - string $criticalBinary = 'critical', array $forceIncludeCssSelectors, + string $criticalBinary = 'critical', ?string $username = null, ?string $password = null ) { diff --git a/src/Service/ProcessManager.php b/src/Service/ProcessManager.php index ff20f3f..86f17c9 100644 --- a/src/Service/ProcessManager.php +++ b/src/Service/ProcessManager.php @@ -166,8 +166,8 @@ public function createProcessesForProvider(ProviderInterface $provider, StoreInt $process = $this->criticalCssService->createCriticalCssProcess( $url, $this->config->getDimensions(), - $this->config->getCriticalBinary(), $this->config->getForceIncludeCssSelectors(), + $this->config->getCriticalBinary(), $this->config->getUsername(), $this->config->getPassword() );