From 254f81e6b8132c7aa0b975c428909f720219aebc Mon Sep 17 00:00:00 2001 From: James McDonald Date: Thu, 25 May 2023 03:02:52 +0000 Subject: [PATCH] Revert Mix dependence on Request, Add unit tests Remove redundant listener tags from phpunit.xml.dist Apply better solution to remove asset timestamping from the cake3 branch Add unit tests to test css() and script() AssetMixHelper methods --- README.md | 3 + config/routes.php | 19 +++--- docs/ServingFromSudirectory.md | 26 ++++++++ phpunit.xml.dist | 11 +--- src/Mix.php | 23 +++++-- stubs/bootstrap/assets/js/app.js | 3 +- .../assets/js/Pages/Pages/Display.js | 2 +- stubs/react/assets/js/app.js | 3 +- stubs/vue/assets/js/app.js | 2 +- .../View/Helper/AssetMixHelperTest.php | 63 ++++++++++++++++++- tests/test_app/config/routes.php | 2 +- 11 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 docs/ServingFromSudirectory.md diff --git a/README.md b/README.md index 433bb74..25c4a34 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,9 @@ bin/cake asset_mix generate inertia-react ```bash bin/cake asset_mix generate react --dir=resources ``` +## Serving CakePHP out of a Subdirectory +Please see [docs/ServingFromSubdirectory](docs/ServingFromSudirectory.md) + ## Version map diff --git a/config/routes.php b/config/routes.php index ad2d5b1..862bdbd 100644 --- a/config/routes.php +++ b/config/routes.php @@ -1,12 +1,13 @@ plugin( - 'AssetMix', - ['path' => '/asset-mix'], - function (RouteBuilder $routes) { - $routes->fallbacks(DashedRoute::class); - }); +/** @var \Cake\Routing\RouteBuilder $routes */ +$routes->plugin( + 'AssetMix', + ['path' => '/asset-mix'], + function (RouteBuilder $routes) { + $routes->fallbacks(DashedRoute::class); + } +); diff --git a/docs/ServingFromSudirectory.md b/docs/ServingFromSudirectory.md new file mode 100644 index 0000000..89e800b --- /dev/null +++ b/docs/ServingFromSudirectory.md @@ -0,0 +1,26 @@ +# Using AssetMix Plugin when Serving CakePHP from a Subdirectory + +To use the AssetMixHelper methods `$this->AssetMix->css('main)` and `$this->AssetMix->script('app')` when CakePHP is being served out of a subdirectory. For example you access your CakePHP application from https://example.com/subdir and NOT https://example.com/ + +Configure `App.base` with `/subdir`: + +```php + // config/app.php + 'App' => [ + 'namespace' => 'App', + 'encoding' => env('APP_ENCODING', 'UTF-8'), + 'defaultLocale' => env('APP_DEFAULT_LOCALE', 'en_AU'), + 'defaultTimezone' => 'Australia/Melbourne', + // comment or remove the default + // 'base' => false, + // hard coded + 'base' => '/subdir', + // or pull from the environment to make it portable + // 'base' => env('SUBDIR', false), + 'language' => 'en', + 'dir' => 'src', + 'webroot' => 'webroot', + 'wwwRoot' => WWW_ROOT, + //... + ] +``` diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4fb1e70..561d10b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -17,14 +17,9 @@ - - - - - - - - + + + diff --git a/src/Mix.php b/src/Mix.php index a03b984..90084b6 100644 --- a/src/Mix.php +++ b/src/Mix.php @@ -3,6 +3,7 @@ namespace AssetMix; +use Cake\Core\Configure; use Exception; /** @@ -35,11 +36,22 @@ public function __invoke($path, $manifestDirectory = ''): string $path = $matches[2]; } - if (! starts_with($path, '/')) { + // strip subdir if exists + $subdir = Configure::read('App.base'); + + if ($subdir) { + $path = preg_replace(sprintf('+^%s+', $subdir), '', $path); + } + + // strip asset timestamp query string + // /js/app.js?1674622148 becomes /js/app.js + $path = preg_replace('/\?.*/', '', $path); + + if (!starts_with($path, '/')) { $path = "/{$path}"; } - if ($manifestDirectory && ! starts_with($manifestDirectory, '/')) { + if ($manifestDirectory && !starts_with($manifestDirectory, '/')) { $manifestDirectory = "/{$manifestDirectory}"; } @@ -59,8 +71,8 @@ public function __invoke($path, $manifestDirectory = ''): string } $manifestPath = WWW_ROOT . $manifestDirectory . '/mix-manifest.json'; - if (! isset(self::$manifests[$manifestPath])) { - if (! file_exists($manifestPath)) { + if (!isset(self::$manifests[$manifestPath])) { + if (!file_exists($manifestPath)) { throw new Exception('The Mix manifest does not exist.'); } @@ -74,7 +86,8 @@ public function __invoke($path, $manifestDirectory = ''): string } $manifest = self::$manifests[$manifestPath]; - if (! isset($manifest[$path])) { + + if (!isset($manifest[$path])) { throw new Exception("Unable to locate AssetMix file: {$path}."); } diff --git a/stubs/bootstrap/assets/js/app.js b/stubs/bootstrap/assets/js/app.js index 9fa1648..da60c04 100644 --- a/stubs/bootstrap/assets/js/app.js +++ b/stubs/bootstrap/assets/js/app.js @@ -4,7 +4,8 @@ try { window.$ = window.jQuery = require('jquery'); require('bootstrap'); -} catch (e) {} +} catch (e) { +} /** * Set CSRF token as a header based on the value of the "XSRF" token cookie. diff --git a/stubs/inertia-react/assets/js/Pages/Pages/Display.js b/stubs/inertia-react/assets/js/Pages/Pages/Display.js index 7480bfd..ba8feea 100644 --- a/stubs/inertia-react/assets/js/Pages/Pages/Display.js +++ b/stubs/inertia-react/assets/js/Pages/Pages/Display.js @@ -5,5 +5,5 @@ import React from 'react'; * when your application has been setup with inertiajs. */ export default function Display() { - return
Hello world!
; + return
Hello world!
; } diff --git a/stubs/react/assets/js/app.js b/stubs/react/assets/js/app.js index aab27d5..6e432ad 100644 --- a/stubs/react/assets/js/app.js +++ b/stubs/react/assets/js/app.js @@ -4,7 +4,8 @@ try { window.$ = window.jQuery = require('jquery'); require('bootstrap'); -} catch (e) {} +} catch (e) { +} /** * Set CSRF token as a header based on the value of the "XSRF" token cookie. diff --git a/stubs/vue/assets/js/app.js b/stubs/vue/assets/js/app.js index e5585e6..cb2d602 100644 --- a/stubs/vue/assets/js/app.js +++ b/stubs/vue/assets/js/app.js @@ -9,5 +9,5 @@ window.Vue = Vue; Vue.component('app-greet', AppGreet); const app = new Vue({ - el: '#app' + el: '#app' }); diff --git a/tests/TestCase/View/Helper/AssetMixHelperTest.php b/tests/TestCase/View/Helper/AssetMixHelperTest.php index badab42..b39ab92 100644 --- a/tests/TestCase/View/Helper/AssetMixHelperTest.php +++ b/tests/TestCase/View/Helper/AssetMixHelperTest.php @@ -7,6 +7,8 @@ use AssetMix\View\Helper\AssetMixHelper; use Cake\Core\Configure; use Cake\Filesystem\Folder; +use Cake\Http\ServerRequest; +use Cake\Routing\Router; use Cake\TestSuite\TestCase; use Cake\View\View; @@ -66,7 +68,7 @@ protected function _copy($withVersion = false) $sourceFilename = 'mix-manifest-with-version.json'; } - if (! copy(COMPARE_PATH . $sourceFilename, WWW_ROOT . $destinationFilename)) { + if (!copy(COMPARE_PATH . $sourceFilename, WWW_ROOT . $destinationFilename)) { throw new \Exception('Unable to copy mix-manifest.json file'); } } @@ -101,7 +103,7 @@ protected function _cleanUp() $files = glob(WWW_ROOT . '*'); foreach ($files as $file) { - if (! is_file($file)) { + if (!is_file($file)) { continue; } @@ -223,4 +225,61 @@ public function testScriptTagWithExternalBaseUrl() $this->assertStringContainsString('https://example.com/js/app.js', $result); } + + /** + * Test `css()` function works when serving out of a /subdir + * + * @return void + */ + public function testRemoveBaseDirIfServingFromSubdirectory() + { + $subdir = '/subdir'; + + Configure::write('App.base', $subdir); + + $request = new ServerRequest([ + 'base' => $subdir, + 'webroot' => $subdir . '/', + ]); + + Router::reload(); + + Router::setRequest($request); + + $this->_copyWithVersion(); + + $result = $this->AssetMix->css('main'); + + $this->assertStringContainsString($subdir . '/css/main.css', $result); + } + + /** + * Test `script()` throws exception when serving from subdir and App.base missing + * + * @return void + */ + public function testSubdirWithoutAppBaseThrowsException() + { + $subdir = '/subdir'; + + Configure::write('App.base', false); + + // url is /subdir/controller/action + $request = new ServerRequest([ + 'base' => $subdir, + 'webroot' => $subdir . '/', + ]); + + Router::reload(); + + Router::setRequest($request); + + $this->_copyWithVersion(); + + $this->expectException(\Exception::class); + + $this->expectExceptionMessage("Unable to locate AssetMix file: {$subdir}/js/app.js."); + + $result = $this->AssetMix->script('app'); + } } diff --git a/tests/test_app/config/routes.php b/tests/test_app/config/routes.php index b94ceb5..cb42ca1 100644 --- a/tests/test_app/config/routes.php +++ b/tests/test_app/config/routes.php @@ -6,6 +6,6 @@ Router::reload(); -Router::scope('/', function (RouteBuilder $routes) { +$routes->scope('/', function (RouteBuilder $routes) { $routes->fallbacks(DashedRoute::class); });