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); });