From 3889a93da1b299d52b7fff84f50178dde7b74a8a Mon Sep 17 00:00:00 2001 From: John D Wells Date: Mon, 9 Aug 2021 14:19:34 -0500 Subject: [PATCH 01/10] Recursively crawl for any entry point imports and output corresponding `` tags. --- src/helpers/ManifestHelper.php | 36 +++++++++++++++++++++++++++++++++- src/services/ViteService.php | 8 ++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/src/helpers/ManifestHelper.php b/src/helpers/ManifestHelper.php index 22c14ef..f83b2df 100644 --- a/src/helpers/ManifestHelper.php +++ b/src/helpers/ManifestHelper.php @@ -150,6 +150,15 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, 'url' => $entry['file'], 'options' => array_merge($scriptOptions, $scriptTagAttrs) ]; + // Include any imports + $importFiles = []; + self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); + foreach ($importFiles as $importFile) { + $tags[] = [ + 'type' => 'import', + 'url' => $importFile, + ]; + } // Include any CSS tags $cssFiles = []; self::extractCssFiles(self::$manifest, $manifestKey, $cssFiles); @@ -167,7 +176,32 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, return $tags; } - /** + /** + * Extract any import files from entries recursively + * + * @param array $manifest + * @param string $manifestKey + * @param array $importFiles + * + * @return array + */ + protected static function extractImportFiles(array $manifest, string $manifestKey, array &$importFiles): array + { + $entry = $manifest[$manifestKey] ?? null; + if (!$entry) { + return []; + } + + $imports = $entry['imports'] ?? []; + foreach($imports as $import) { + $importFiles[] = $manifest[$import]['file']; + self::extractImportFiles($manifest, $import, $importFiles); + } + + return $importFiles; + } + + /** * Extract any CSS files from entries recursively * * @param array $manifest diff --git a/src/services/ViteService.php b/src/services/ViteService.php index a81370e..c1aa2f6 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -210,10 +210,18 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } $this->manifestShimsIncluded = true; } + foreach(array_merge($tags, $legacyTags) as $tag) { if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { + case 'import': + $lines[] = HtmlHelper::tag('link', '', [ + 'rel' => 'modulepreload', + 'crossorigin' => true, + 'href' => $url + ]); + break; case 'file': $lines[] = HtmlHelper::jsFile($url, $tag['options']); break; From 2ebac89292cba3219e1458c5ebfb1f5890012b40 Mon Sep 17 00:00:00 2001 From: John D Wells Date: Mon, 9 Aug 2021 15:42:53 -0500 Subject: [PATCH 02/10] Match crossorigin of the entry tag. --- src/helpers/ManifestHelper.php | 4 +++- src/services/ViteService.php | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/helpers/ManifestHelper.php b/src/helpers/ManifestHelper.php index f83b2df..6904187 100644 --- a/src/helpers/ManifestHelper.php +++ b/src/helpers/ManifestHelper.php @@ -145,10 +145,11 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, continue; } // Include the entry script + $tagOptions = array_merge($scriptOptions, $scriptTagAttrs); $tags[] = [ 'type' => 'file', 'url' => $entry['file'], - 'options' => array_merge($scriptOptions, $scriptTagAttrs) + 'options' => $tagOptions ]; // Include any imports $importFiles = []; @@ -157,6 +158,7 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, $tags[] = [ 'type' => 'import', 'url' => $importFile, + 'crossorigin' => $tagOptions['crossorigin'] ?? true ]; } // Include any CSS tags diff --git a/src/services/ViteService.php b/src/services/ViteService.php index c1aa2f6..19d55bd 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -218,8 +218,8 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip case 'import': $lines[] = HtmlHelper::tag('link', '', [ 'rel' => 'modulepreload', - 'crossorigin' => true, - 'href' => $url + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, ]); break; case 'file': From e3ee5835ceeb78e17b12dfc92a3e992ee8f2e132 Mon Sep 17 00:00:00 2001 From: John D Wells Date: Mon, 9 Aug 2021 15:44:50 -0500 Subject: [PATCH 03/10] Alpha sort --- src/helpers/ManifestHelper.php | 2 +- src/services/ViteService.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/helpers/ManifestHelper.php b/src/helpers/ManifestHelper.php index 6904187..2ea993c 100644 --- a/src/helpers/ManifestHelper.php +++ b/src/helpers/ManifestHelper.php @@ -156,9 +156,9 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); foreach ($importFiles as $importFile) { $tags[] = [ + 'crossorigin' => $tagOptions['crossorigin'] ?? true, 'type' => 'import', 'url' => $importFile, - 'crossorigin' => $tagOptions['crossorigin'] ?? true ]; } // Include any CSS tags diff --git a/src/services/ViteService.php b/src/services/ViteService.php index 19d55bd..c5c02a0 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -217,9 +217,9 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip switch ($tag['type']) { case 'import': $lines[] = HtmlHelper::tag('link', '', [ - 'rel' => 'modulepreload', 'crossorigin' => $tag['crossorigin'], 'href' => $url, + 'rel' => 'modulepreload', ]); break; case 'file': From 4716259e70baad72546465d7042db04f1ba24668 Mon Sep 17 00:00:00 2001 From: John D Wells Date: Tue, 10 Aug 2021 05:50:33 -0500 Subject: [PATCH 04/10] Implement modulepreload for `register()`. --- src/services/ViteService.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/services/ViteService.php b/src/services/ViteService.php index c5c02a0..fba7799 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -339,6 +339,16 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { + case 'import': + $view->registerLinkTag( + [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ], + md5($url) + ); + break; case 'file': $view->registerJsFile( $url, From 582b7a8f5955caade167d0b3040f478108b38500 Mon Sep 17 00:00:00 2001 From: John D Wells Date: Tue, 10 Aug 2021 07:12:38 -0500 Subject: [PATCH 05/10] Refactor to prevent adding `` tags for imports coming from legacy entry files. Also introduce polyfill, for browsers that support type="module" only. --- src/services/ViteService.php | 81 +++++++++++++++---- .../assets/dist/modulepreload-polyfill.min.js | 1 + src/web/assets/src/modulepreload-polyfill.js | 72 +++++++++++++++++ 3 files changed, 137 insertions(+), 17 deletions(-) create mode 100644 src/web/assets/dist/modulepreload-polyfill.min.js create mode 100644 src/web/assets/src/modulepreload-polyfill.js diff --git a/src/services/ViteService.php b/src/services/ViteService.php index fba7799..3368689 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -101,6 +101,11 @@ class ViteService extends Component */ protected $devServerShimsIncluded = false; + /** + * @var bool Whether the modulepreload polyfill has been included yet or not + */ + protected $modulepreloadPolyfillIncluded = false; + // Public Methods // ========================================================================= @@ -215,13 +220,6 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { - case 'import': - $lines[] = HtmlHelper::tag('link', '', [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ]); - break; case 'file': $lines[] = HtmlHelper::jsFile($url, $tag['options']); break; @@ -234,6 +232,33 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } } + // modulepreload any imports from modern tags + $modulepreloadPolyfillRequired = false; + foreach($tags as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'import': + $lines[] = HtmlHelper::tag('link', '', [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ]); + $modulepreloadPolyfillRequired = true; + break; + default: + break; + } + } + } + if($modulepreloadPolyfillRequired && ! $this->modulepreloadPolyfillIncluded) { + $this->modulepreloadPolyfillIncluded = true; + $lines[] = HtmlHelper::script( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + ['type' => 'module'] + ); + } + return implode("\r\n", $lines); } @@ -339,16 +364,6 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { - case 'import': - $view->registerLinkTag( - [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ], - md5($url) - ); - break; case 'file': $view->registerJsFile( $url, @@ -367,6 +382,38 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } } } + + // modulepreload any imports from modern tags + $modulepreloadPolyfillRequired = false; + foreach($tags as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'import': + $view->registerLinkTag( + [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ], + md5($url) + ); + $modulepreloadPolyfillRequired = true; + break; + default: + break; + } + } + } + if($modulepreloadPolyfillRequired && ! $this->modulepreloadPolyfillIncluded) { + $this->modulepreloadPolyfillIncluded = true; + $view->registerScript( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + $view::POS_HEAD, + ['type' => 'module'], + 'MODULEPRELOAD_POLYFILL' + ); + } } /** diff --git a/src/web/assets/dist/modulepreload-polyfill.min.js b/src/web/assets/dist/modulepreload-polyfill.min.js new file mode 100644 index 0000000..66b72d0 --- /dev/null +++ b/src/web/assets/dist/modulepreload-polyfill.min.js @@ -0,0 +1 @@ +!function(){const e=document.createElement("link").relList;if(!(e&&e.supports&&e.supports("modulepreload"))){for(const e of document.querySelectorAll('link[rel="modulepreload"]'))r(e);new MutationObserver((e=>{for(const o of e)if("childList"===o.type)for(const e of o.addedNodes)if("LINK"===e.tagName&&"modulepreload"===e.rel)r(e);else if(e.querySelectorAll)for(const o of e.querySelectorAll("link[rel=modulepreload]"))r(o)})).observe(document,{childList:!0,subtree:!0})}function r(e){if(e.ep)return;e.ep=!0;const r=function(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerpolicy&&(r.referrerPolicy=e.referrerpolicy),"use-credentials"===e.crossorigin?r.credentials="include":"anonymous"===e.crossorigin?r.credentials="omit":r.credentials="same-origin",r}(e);fetch(e.href,r)}}(); \ No newline at end of file diff --git a/src/web/assets/src/modulepreload-polyfill.js b/src/web/assets/src/modulepreload-polyfill.js new file mode 100644 index 0000000..1885823 --- /dev/null +++ b/src/web/assets/src/modulepreload-polyfill.js @@ -0,0 +1,72 @@ +/** + The following polyfill function is meant to run in the browser and adapted from + https://github.com/guybedford/es-module-shims + MIT License + Copyright (C) 2018-2021 Guy Bedford + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + */ + +(function() { + const relList = document.createElement('link').relList; + if (relList && relList.supports && relList.supports('modulepreload')) { + return; + } + + for (const link of document.querySelectorAll('link[rel="modulepreload"]')) { + processPreload(link); + } + + new MutationObserver(mutations => { + for (const mutation of mutations) { + if (mutation.type !== 'childList') continue; + for (const node of mutation.addedNodes) { + if (node.tagName === 'LINK' && node.rel === 'modulepreload') + processPreload(node); + else if (node.querySelectorAll) { + for (const link of node.querySelectorAll('link[rel=modulepreload]')) { + processPreload(link); + } + } + } + } + }).observe(document, { childList: true, subtree: true }); + + function getFetchOpts (script) { + const fetchOpts = {}; + if (script.integrity) + fetchOpts.integrity = script.integrity; + if (script.referrerpolicy) + fetchOpts.referrerPolicy = script.referrerpolicy; + if (script.crossorigin === 'use-credentials') + fetchOpts.credentials = 'include'; + else if (script.crossorigin === 'anonymous') + fetchOpts.credentials = 'omit'; + else + fetchOpts.credentials = 'same-origin'; + return fetchOpts; + } + + function processPreload(script) { + if (script.ep) { + // ep marker = processed + return; + } + script.ep = true; + // prepopulate the load record + const fetchOpts = getFetchOpts(script); + fetch(script.href, fetchOpts); + } +}()); From 059ea9629f87cf7605796d44ad5afb2845a8ed0b Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 10 Aug 2021 12:50:26 -0400 Subject: [PATCH 06/10] Code formatting --- src/helpers/ManifestHelper.php | 70 +++++++++---------- src/services/ViteService.php | 118 ++++++++++++++++----------------- 2 files changed, 94 insertions(+), 94 deletions(-) diff --git a/src/helpers/ManifestHelper.php b/src/helpers/ManifestHelper.php index 2ea993c..e917021 100644 --- a/src/helpers/ManifestHelper.php +++ b/src/helpers/ManifestHelper.php @@ -152,15 +152,15 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, 'options' => $tagOptions ]; // Include any imports - $importFiles = []; - self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); - foreach ($importFiles as $importFile) { - $tags[] = [ - 'crossorigin' => $tagOptions['crossorigin'] ?? true, - 'type' => 'import', - 'url' => $importFile, - ]; - } + $importFiles = []; + self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); + foreach ($importFiles as $importFile) { + $tags[] = [ + 'crossorigin' => $tagOptions['crossorigin'] ?? true, + 'type' => 'import', + 'url' => $importFile, + ]; + } // Include any CSS tags $cssFiles = []; self::extractCssFiles(self::$manifest, $manifestKey, $cssFiles); @@ -178,32 +178,32 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, return $tags; } - /** - * Extract any import files from entries recursively - * - * @param array $manifest - * @param string $manifestKey - * @param array $importFiles - * - * @return array - */ - protected static function extractImportFiles(array $manifest, string $manifestKey, array &$importFiles): array - { - $entry = $manifest[$manifestKey] ?? null; - if (!$entry) { - return []; - } - - $imports = $entry['imports'] ?? []; - foreach($imports as $import) { - $importFiles[] = $manifest[$import]['file']; - self::extractImportFiles($manifest, $import, $importFiles); - } - - return $importFiles; - } - - /** + /** + * Extract any import files from entries recursively + * + * @param array $manifest + * @param string $manifestKey + * @param array $importFiles + * + * @return array + */ + protected static function extractImportFiles(array $manifest, string $manifestKey, array &$importFiles): array + { + $entry = $manifest[$manifestKey] ?? null; + if (!$entry) { + return []; + } + + $imports = $entry['imports'] ?? []; + foreach ($imports as $import) { + $importFiles[] = $manifest[$import]['file']; + self::extractImportFiles($manifest, $import, $importFiles); + } + + return $importFiles; + } + + /** * Extract any CSS files from entries recursively * * @param array $manifest diff --git a/src/services/ViteService.php b/src/services/ViteService.php index 3368689..f2fe837 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -165,7 +165,7 @@ public function devServerScript(string $path, array $scriptTagAttrs = []): strin // Replace the hard-coded dev server URL with whatever they have theirs set to $script = str_replace( 'http://localhost:3000/', - rtrim($this->devServerPublic, '/').'/', + rtrim($this->devServerPublic, '/') . '/', $script ); $lines[] = HtmlHelper::script( @@ -216,7 +216,7 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip $this->manifestShimsIncluded = true; } - foreach(array_merge($tags, $legacyTags) as $tag) { + foreach (array_merge($tags, $legacyTags) as $tag) { if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { @@ -233,30 +233,30 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } // modulepreload any imports from modern tags - $modulepreloadPolyfillRequired = false; - foreach($tags as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { - case 'import': - $lines[] = HtmlHelper::tag('link', '', [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ]); - $modulepreloadPolyfillRequired = true; - break; - default: - break; - } - } - } - if($modulepreloadPolyfillRequired && ! $this->modulepreloadPolyfillIncluded) { - $this->modulepreloadPolyfillIncluded = true; - $lines[] = HtmlHelper::script( - FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), - ['type' => 'module'] - ); + $modulepreloadPolyfillRequired = false; + foreach ($tags as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'import': + $lines[] = HtmlHelper::tag('link', '', [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ]); + $modulepreloadPolyfillRequired = true; + break; + default: + break; + } + } + } + if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { + $this->modulepreloadPolyfillIncluded = true; + $lines[] = HtmlHelper::script( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + ['type' => 'module'] + ); } return implode("\r\n", $lines); @@ -305,7 +305,7 @@ public function devServerRegister(string $path, array $scriptTagAttrs = []) // Replace the hard-coded dev server URL with whatever they have theirs set to $script = str_replace( 'http://localhost:3000/', - rtrim($this->devServerPublic, '/').'/', + rtrim($this->devServerPublic, '/') . '/', $script ); $view->registerScript( @@ -360,7 +360,7 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } $this->manifestShimsIncluded = true; } - foreach(array_merge($tags, $legacyTags) as $tag) { + foreach (array_merge($tags, $legacyTags) as $tag) { if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); switch ($tag['type']) { @@ -383,37 +383,37 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } } - // modulepreload any imports from modern tags - $modulepreloadPolyfillRequired = false; - foreach($tags as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { - case 'import': - $view->registerLinkTag( - [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ], - md5($url) - ); - $modulepreloadPolyfillRequired = true; - break; - default: - break; - } - } - } - if($modulepreloadPolyfillRequired && ! $this->modulepreloadPolyfillIncluded) { - $this->modulepreloadPolyfillIncluded = true; - $view->registerScript( - FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), - $view::POS_HEAD, - ['type' => 'module'], - 'MODULEPRELOAD_POLYFILL' - ); - } + // modulepreload any imports from modern tags + $modulepreloadPolyfillRequired = false; + foreach ($tags as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'import': + $view->registerLinkTag( + [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ], + md5($url) + ); + $modulepreloadPolyfillRequired = true; + break; + default: + break; + } + } + } + if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { + $this->modulepreloadPolyfillIncluded = true; + $view->registerScript( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + $view::POS_HEAD, + ['type' => 'module'], + 'MODULEPRELOAD_POLYFILL' + ); + } } /** From a64540bef1891bb61838114b0f0f5cc8b05556a2 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 10 Aug 2021 13:04:35 -0400 Subject: [PATCH 07/10] Refactor to a single tag loop, possible because only non-legacy tags ever include imports --- src/helpers/ManifestHelper.php | 17 ++++++++++------- src/services/ViteService.php | 28 ++-------------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/src/helpers/ManifestHelper.php b/src/helpers/ManifestHelper.php index e917021..22862d7 100644 --- a/src/helpers/ManifestHelper.php +++ b/src/helpers/ManifestHelper.php @@ -153,13 +153,16 @@ public static function extractManifestTags(string $path, bool $asyncCss = true, ]; // Include any imports $importFiles = []; - self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); - foreach ($importFiles as $importFile) { - $tags[] = [ - 'crossorigin' => $tagOptions['crossorigin'] ?? true, - 'type' => 'import', - 'url' => $importFile, - ]; + // Only include import tags for the non-legacy scripts + if (!$legacy) { + self::extractImportFiles(self::$manifest, $manifestKey, $importFiles); + foreach ($importFiles as $importFile) { + $tags[] = [ + 'crossorigin' => $tagOptions['crossorigin'] ?? true, + 'type' => 'import', + 'url' => $importFile, + ]; + } } // Include any CSS tags $cssFiles = []; diff --git a/src/services/ViteService.php b/src/services/ViteService.php index f2fe837..961fc31 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -226,31 +226,19 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip case 'css': $lines[] = HtmlHelper::cssFile($url, $tag['options']); break; - default: - break; - } - } - } - - // modulepreload any imports from modern tags - $modulepreloadPolyfillRequired = false; - foreach ($tags as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { case 'import': $lines[] = HtmlHelper::tag('link', '', [ 'crossorigin' => $tag['crossorigin'], 'href' => $url, 'rel' => 'modulepreload', ]); - $modulepreloadPolyfillRequired = true; break; default: break; } } } + if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { $this->modulepreloadPolyfillIncluded = true; $lines[] = HtmlHelper::script( @@ -377,18 +365,6 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr $tag['options'] ); break; - default: - break; - } - } - } - - // modulepreload any imports from modern tags - $modulepreloadPolyfillRequired = false; - foreach ($tags as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { case 'import': $view->registerLinkTag( [ @@ -398,13 +374,13 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr ], md5($url) ); - $modulepreloadPolyfillRequired = true; break; default: break; } } } + if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { $this->modulepreloadPolyfillIncluded = true; $view->registerScript( From 9abd2fb7f309c7cb77a636e12cb177cc2d59ea12 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 10 Aug 2021 13:31:18 -0400 Subject: [PATCH 08/10] Refactor to includeModulePreloadShim --- src/services/ViteService.php | 47 ++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 24 deletions(-) diff --git a/src/services/ViteService.php b/src/services/ViteService.php index 961fc31..5851f23 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -88,6 +88,11 @@ class ViteService extends Component */ public $includeReactRefreshShim = false; + /** + * @var bool Whether the modulepreload-polyfill shim should be included + */ + public $includeModulePreloadShim = true; + // Protected Properties // ========================================================================= @@ -101,11 +106,6 @@ class ViteService extends Component */ protected $devServerShimsIncluded = false; - /** - * @var bool Whether the modulepreload polyfill has been included yet or not - */ - protected $modulepreloadPolyfillIncluded = false; - // Public Methods // ========================================================================= @@ -204,6 +204,13 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip $legacyTags = ManifestHelper::legacyManifestTags($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs); // Include any manifest shims if (!$this->manifestShimsIncluded) { + // Handle the modulepreload-polyfill shim + if ($this->includeModulePreloadShim) { + $lines[] = HtmlHelper::script( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + ['type' => 'module'] + ); + } // Handle any legacy polyfills if (!empty($legacyTags)) { $lines[] = HtmlHelper::script( @@ -215,7 +222,7 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } $this->manifestShimsIncluded = true; } - + // Iterate through all the tags, and include them foreach (array_merge($tags, $legacyTags) as $tag) { if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); @@ -239,14 +246,6 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } } - if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { - $this->modulepreloadPolyfillIncluded = true; - $lines[] = HtmlHelper::script( - FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), - ['type' => 'module'] - ); - } - return implode("\r\n", $lines); } @@ -335,6 +334,15 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr $legacyTags = ManifestHelper::legacyManifestTags($path, $asyncCss, $scriptTagAttrs, $cssTagAttrs); // Include any manifest shims if (!$this->manifestShimsIncluded) { + // Handle the modulepreload-polyfill shim + if ($this->includeModulePreloadShim) { + $view->registerScript( + FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), + $view::POS_HEAD, + ['type' => 'module'], + 'MODULEPRELOAD_POLYFILL' + ); + } // Handle any legacy polyfills if (!empty($legacyTags)) { $view->registerScript( @@ -348,6 +356,7 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } $this->manifestShimsIncluded = true; } + // Iterate through all the tags, and include them foreach (array_merge($tags, $legacyTags) as $tag) { if (!empty($tag)) { $url = FileHelper::createUrl($this->serverPublic, $tag['url']); @@ -380,16 +389,6 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } } } - - if ($modulepreloadPolyfillRequired && !$this->modulepreloadPolyfillIncluded) { - $this->modulepreloadPolyfillIncluded = true; - $view->registerScript( - FileHelper::fetchScript('modulepreload-polyfill.min.js', $this->cacheKeySuffix), - $view::POS_HEAD, - ['type' => 'module'], - 'MODULEPRELOAD_POLYFILL' - ); - } } /** From 09751f4d3cc5f120d30d5330b1980004b3b93019 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 10 Aug 2021 14:32:37 -0400 Subject: [PATCH 09/10] Refactor to functions --- src/services/ViteService.php | 139 +++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 56 deletions(-) diff --git a/src/services/ViteService.php b/src/services/ViteService.php index 5851f23..291eea2 100755 --- a/src/services/ViteService.php +++ b/src/services/ViteService.php @@ -222,29 +222,7 @@ public function manifestScript(string $path, bool $asyncCss = true, array $scrip } $this->manifestShimsIncluded = true; } - // Iterate through all the tags, and include them - foreach (array_merge($tags, $legacyTags) as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { - case 'file': - $lines[] = HtmlHelper::jsFile($url, $tag['options']); - break; - case 'css': - $lines[] = HtmlHelper::cssFile($url, $tag['options']); - break; - case 'import': - $lines[] = HtmlHelper::tag('link', '', [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ]); - break; - default: - break; - } - } - } + $lines = array_merge($lines, $this->manifestScriptTags($tags, $legacyTags)); return implode("\r\n", $lines); } @@ -356,39 +334,7 @@ public function manifestRegister(string $path, bool $asyncCss = true, array $scr } $this->manifestShimsIncluded = true; } - // Iterate through all the tags, and include them - foreach (array_merge($tags, $legacyTags) as $tag) { - if (!empty($tag)) { - $url = FileHelper::createUrl($this->serverPublic, $tag['url']); - switch ($tag['type']) { - case 'file': - $view->registerJsFile( - $url, - $tag['options'], - md5($url . JsonHelper::encode($tag['options'])) - ); - break; - case 'css': - $view->registerCssFile( - $url, - $tag['options'] - ); - break; - case 'import': - $view->registerLinkTag( - [ - 'crossorigin' => $tag['crossorigin'], - 'href' => $url, - 'rel' => 'modulepreload', - ], - md5($url) - ); - break; - default: - break; - } - } - } + $this->manifestRegisterTags($tags, $legacyTags); } /** @@ -475,4 +421,85 @@ protected function injectErrorEntry() // That's okay, Vite will have already logged the error } } + + + /** + * Iterate through all the tags, and return them + * + * @param array $tags + * @param array $legacyTags + * @return array + */ + protected function manifestScriptTags(array $tags, array $legacyTags): array + { + $lines = []; + foreach (array_merge($tags, $legacyTags) as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'file': + $lines[] = HtmlHelper::jsFile($url, $tag['options']); + break; + case 'css': + $lines[] = HtmlHelper::cssFile($url, $tag['options']); + break; + case 'import': + $lines[] = HtmlHelper::tag('link', '', [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ]); + break; + default: + break; + } + } + } + + return $lines; + } + + /** + * Iterate through all the tags, and register them + * + * @param array $tags + * @param array $legacyTags + * @throws InvalidConfigException + */ + protected function manifestRegisterTags(array $tags, array $legacyTags) + { + $view = Craft::$app->getView(); + foreach (array_merge($tags, $legacyTags) as $tag) { + if (!empty($tag)) { + $url = FileHelper::createUrl($this->serverPublic, $tag['url']); + switch ($tag['type']) { + case 'file': + $view->registerJsFile( + $url, + $tag['options'], + md5($url . JsonHelper::encode($tag['options'])) + ); + break; + case 'css': + $view->registerCssFile( + $url, + $tag['options'] + ); + break; + case 'import': + $view->registerLinkTag( + [ + 'crossorigin' => $tag['crossorigin'], + 'href' => $url, + 'rel' => 'modulepreload', + ], + md5($url) + ); + break; + default: + break; + } + } + } + } } From 668fb1072158a9608a50c6f477ce437edbed04f0 Mon Sep 17 00:00:00 2001 From: Andrew Welch Date: Tue, 10 Aug 2021 14:37:22 -0400 Subject: [PATCH 10/10] Version 1.0.11 --- CHANGELOG.md | 16 ++++++++++------ composer.json | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69a9500..dab9342 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,24 +1,28 @@ # Plugin Vite Changelog +## 1.0.11 - 2021.08.10 +### Added +* Added [Preload Directives Generation](https://vitejs.dev/guide/features.html#preload-directives-generation) that will automatically generate `` directives for entry chunks and their direct imports ([PR#2](https://github.com/nystudio107/craft-plugin-vite/pull/2)) + ## 1.0.10 - 2021.07.14 ### Added -* Added a `craft.vite.devServerRunning()` method to allow you to determine if the Vite dev server is running or not from your Twig templates (https://github.com/nystudio107/craft-vite/issues/10) +* Added a `craft.vite.devServerRunning()` method to allow you to determine if the Vite dev server is running or not from your Twig templates ([#10](https://github.com/nystudio107/craft-vite/issues/10)) ## 1.0.9 - 2021.07.14 ### Changed -* Switched the `checkDevServer` test file to `@vite/client` to accommodate with the change in Vite `^2.4.0` to use the `.mjs` extension (https://github.com/nystudio107/craft-vite/issues/11) +* Switched the `checkDevServer` test file to `@vite/client` to accommodate with the change in Vite `^2.4.0` to use the `.mjs` extension ([#11](https://github.com/nystudio107/craft-vite/issues/11)) ## 1.0.8 - 2021.06.29 ### Changed -* Roll back the automatic inclusion of `@vite/client.js` (https://github.com/nystudio107/craft-vite/issues/9) +* Roll back the automatic inclusion of `@vite/client.js` ([#9](https://github.com/nystudio107/craft-vite/issues/9)) ## 1.0.7 - 2021.06.28 ### Changed -* Always include the `@vite/client.js` script if the dev server is running (https://github.com/nystudio107/craft-vite/issues/9) +* Always include the `@vite/client.js` script if the dev server is running ([#9](https://github.com/nystudio107/craft-vite/issues/9)) ## 1.0.6 - 2021.05.21 ### Added -* Added a `includeReactRefreshShim` setting that will automatically include the required [shim for `react-refresh`](https://vitejs.dev/guide/backend-integration.html#backend-integration) when the Vite dev server is running (https://github.com/nystudio107/craft-vite/issues/5) +* Added a `includeReactRefreshShim` setting that will automatically include the required [shim for `react-refresh`](https://vitejs.dev/guide/backend-integration.html#backend-integration) when the Vite dev server is running ([#5](https://github.com/nystudio107/craft-vite/issues/5)) ### Changed * Removed custom user/agent header that was a holdover from `curl` @@ -43,7 +47,7 @@ ## 1.0.3 - 2021.05.08 ### Added -* Added the `devServerInternal` setting back in, along with `checkDevServer` for people who want the fallback behavior (https://github.com/nystudio107/craft-vite/issues/2) +* Added the `devServerInternal` setting back in, along with `checkDevServer` for people who want the fallback behavior ([#2](https://github.com/nystudio107/craft-vite/issues/2)) ### Changed * Refactored `extractCssFiles()` to be simpler diff --git a/composer.json b/composer.json index 928b99d..398bdcc 100644 --- a/composer.json +++ b/composer.json @@ -1,7 +1,7 @@ { "name": "nystudio107/craft-plugin-vite", "description": "Plugin Vite is the conduit between Craft CMS plugins and Vite, with manifest.json & HMR support", - "version": "1.0.10", + "version": "1.0.11", "keywords": [ "craftcms", "plugin",