diff --git a/composer.json b/composer.json index 8395e4a..be34835 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,12 @@ "homepage": "https://github.com/fxpio/foxy", "type": "composer-plugin", "license": "MIT", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/terabytesoftw" + } + ], "require": { "ext-ctype": "*", "ext-mbstring": "*", diff --git a/composer.lock b/composer.lock index d98a29c..da7e3e4 100644 --- a/composer.lock +++ b/composer.lock @@ -84,16 +84,16 @@ }, { "name": "composer/class-map-generator", - "version": "1.1.1", + "version": "1.3.2", "source": { "type": "git", "url": "https://github.com/composer/class-map-generator.git", - "reference": "8286a62d243312ed99b3eee20d5005c961adb311" + "reference": "acd227952154850d0bb7d65caa4f9edf9cd806a7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/class-map-generator/zipball/8286a62d243312ed99b3eee20d5005c961adb311", - "reference": "8286a62d243312ed99b3eee20d5005c961adb311", + "url": "https://api.github.com/repos/composer/class-map-generator/zipball/acd227952154850d0bb7d65caa4f9edf9cd806a7", + "reference": "acd227952154850d0bb7d65caa4f9edf9cd806a7", "shasum": "" }, "require": { @@ -137,7 +137,7 @@ ], "support": { "issues": "https://github.com/composer/class-map-generator/issues", - "source": "https://github.com/composer/class-map-generator/tree/1.1.1" + "source": "https://github.com/composer/class-map-generator/tree/1.3.2" }, "funding": [ { @@ -153,7 +153,7 @@ "type": "tidelift" } ], - "time": "2024-03-15T12:53:41+00:00" + "time": "2024-05-31T19:45:56+00:00" }, { "name": "composer/composer", @@ -340,16 +340,16 @@ }, { "name": "composer/pcre", - "version": "3.1.3", + "version": "3.1.4", "source": { "type": "git", "url": "https://github.com/composer/pcre.git", - "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8" + "reference": "04229f163664973f68f38f6f73d917799168ef24" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", - "reference": "5b16e25a5355f1f3afdfc2f954a0a80aec4826a8", + "url": "https://api.github.com/repos/composer/pcre/zipball/04229f163664973f68f38f6f73d917799168ef24", + "reference": "04229f163664973f68f38f6f73d917799168ef24", "shasum": "" }, "require": { @@ -391,7 +391,7 @@ ], "support": { "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.1.3" + "source": "https://github.com/composer/pcre/tree/3.1.4" }, "funding": [ { @@ -407,7 +407,7 @@ "type": "tidelift" } ], - "time": "2024-03-19T10:26:25+00:00" + "time": "2024-05-27T13:40:54+00:00" }, { "name": "composer/semver", @@ -811,16 +811,16 @@ }, { "name": "react/promise", - "version": "v3.1.0", + "version": "v3.2.0", "source": { "type": "git", "url": "https://github.com/reactphp/promise.git", - "reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c" + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/e563d55d1641de1dea9f5e84f3cccc66d2bfe02c", - "reference": "e563d55d1641de1dea9f5e84f3cccc66d2bfe02c", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", "shasum": "" }, "require": { @@ -872,7 +872,7 @@ ], "support": { "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.1.0" + "source": "https://github.com/reactphp/promise/tree/v3.2.0" }, "funding": [ { @@ -880,7 +880,7 @@ "type": "open_collective" } ], - "time": "2023-11-16T16:21:57+00:00" + "time": "2024-05-24T10:39:05+00:00" }, { "name": "seld/jsonlint", @@ -1057,16 +1057,16 @@ }, { "name": "symfony/console", - "version": "v6.4.7", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f" + "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", - "reference": "a170e64ae10d00ba89e2acbb590dc2e54da8ad8f", + "url": "https://api.github.com/repos/symfony/console/zipball/be5854cee0e8c7b110f00d695d11debdfa1a2a91", + "reference": "be5854cee0e8c7b110f00d695d11debdfa1a2a91", "shasum": "" }, "require": { @@ -1131,7 +1131,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.7" + "source": "https://github.com/symfony/console/tree/v6.4.8" }, "funding": [ { @@ -1147,7 +1147,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:22:46+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1218,23 +1218,25 @@ }, { "name": "symfony/filesystem", - "version": "v6.4.7", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4" + "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/78dde75f8f6dbbca4ec436a4b0087f7af02076d4", - "reference": "78dde75f8f6dbbca4ec436a4b0087f7af02076d4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4d37529150e7081c51b3c5d5718c55a04a9503f3", + "reference": "4d37529150e7081c51b3c5d5718c55a04a9503f3", "shasum": "" }, "require": { "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8", - "symfony/process": "^5.4|^6.4" + "symfony/polyfill-mbstring": "~1.8" + }, + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -1262,7 +1264,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.7" + "source": "https://github.com/symfony/filesystem/tree/v6.4.8" }, "funding": [ { @@ -1278,20 +1280,20 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:22:46+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/finder", - "version": "v6.4.7", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "511c48990be17358c23bf45c5d71ab85d40fb764" + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/511c48990be17358c23bf45c5d71ab85d40fb764", - "reference": "511c48990be17358c23bf45c5d71ab85d40fb764", + "url": "https://api.github.com/repos/symfony/finder/zipball/3ef977a43883215d560a2cecb82ec8e62131471c", + "reference": "3ef977a43883215d560a2cecb82ec8e62131471c", "shasum": "" }, "require": { @@ -1326,7 +1328,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.7" + "source": "https://github.com/symfony/finder/tree/v6.4.8" }, "funding": [ { @@ -1342,7 +1344,7 @@ "type": "tidelift" } ], - "time": "2024-04-23T10:36:43+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/polyfill-ctype", @@ -1896,16 +1898,16 @@ }, { "name": "symfony/process", - "version": "v6.4.7", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "cdb1c81c145fd5aa9b0038bab694035020943381" + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/cdb1c81c145fd5aa9b0038bab694035020943381", - "reference": "cdb1c81c145fd5aa9b0038bab694035020943381", + "url": "https://api.github.com/repos/symfony/process/zipball/8d92dd79149f29e89ee0f480254db595f6a6a2c5", + "reference": "8d92dd79149f29e89ee0f480254db595f6a6a2c5", "shasum": "" }, "require": { @@ -1937,7 +1939,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.7" + "source": "https://github.com/symfony/process/tree/v6.4.8" }, "funding": [ { @@ -1953,7 +1955,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:22:46+00:00" + "time": "2024-05-31T14:49:08+00:00" }, { "name": "symfony/service-contracts", @@ -2040,16 +2042,16 @@ }, { "name": "symfony/string", - "version": "v6.4.7", + "version": "v6.4.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69" + "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/ffeb9591c61f65a68d47f77d12b83fa530227a69", - "reference": "ffeb9591c61f65a68d47f77d12b83fa530227a69", + "url": "https://api.github.com/repos/symfony/string/zipball/a147c0f826c4a1f3afb763ab8e009e37c877a44d", + "reference": "a147c0f826c4a1f3afb763ab8e009e37c877a44d", "shasum": "" }, "require": { @@ -2106,7 +2108,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.7" + "source": "https://github.com/symfony/string/tree/v6.4.8" }, "funding": [ { @@ -2122,7 +2124,7 @@ "type": "tidelift" } ], - "time": "2024-04-18T09:22:46+00:00" + "time": "2024-05-31T14:49:08+00:00" } ], "packages-dev": [ @@ -3126,16 +3128,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.29.0", + "version": "1.29.1", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc" + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/536889f2b340489d328f5ffb7b02bb6b183ddedc", - "reference": "536889f2b340489d328f5ffb7b02bb6b183ddedc", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4", + "reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4", "shasum": "" }, "require": { @@ -3167,9 +3169,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.0" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1" }, - "time": "2024-05-06T12:04:23+00:00" + "time": "2024-05-31T08:52:43+00:00" }, { "name": "phpunit/php-code-coverage", @@ -4579,16 +4581,16 @@ }, { "name": "symplify/easy-coding-standard", - "version": "12.1.14", + "version": "12.2.1", "source": { "type": "git", "url": "https://github.com/easy-coding-standard/easy-coding-standard.git", - "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5" + "reference": "095fe591b2e51fd84edd21b8c9be74402eadc50e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/e3c4a241ee36704f7cf920d5931f39693e64afd5", - "reference": "e3c4a241ee36704f7cf920d5931f39693e64afd5", + "url": "https://api.github.com/repos/easy-coding-standard/easy-coding-standard/zipball/095fe591b2e51fd84edd21b8c9be74402eadc50e", + "reference": "095fe591b2e51fd84edd21b8c9be74402eadc50e", "shasum": "" }, "require": { @@ -4599,6 +4601,9 @@ "phpcsstandards/php_codesniffer": "<3.8", "symplify/coding-standard": "<12.1" }, + "suggest": { + "ext-dom": "Needed to support checkstyle output format in class CheckstyleOutputFormatter" + }, "bin": [ "bin/ecs" ], @@ -4621,7 +4626,7 @@ ], "support": { "issues": "https://github.com/easy-coding-standard/easy-coding-standard/issues", - "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.1.14" + "source": "https://github.com/easy-coding-standard/easy-coding-standard/tree/12.2.1" }, "funding": [ { @@ -4633,7 +4638,7 @@ "type": "github" } ], - "time": "2024-02-23T13:10:40+00:00" + "time": "2024-06-02T01:25:21+00:00" }, { "name": "theseer/tokenizer", diff --git a/resources/doc/config.md b/resources/doc/config.md index cea8c62..5c5ea51 100644 --- a/resources/doc/config.md +++ b/resources/doc/config.md @@ -369,3 +369,18 @@ If you do not deactivate any packages, you can use a simple array. } } ``` + +### Specify a custom directory for `package.json` file + +You can define the custom directory for `package.json` file with the option `root-package-json-dir` [`string`, default: `null`]. + +**Example:** +```json +{ + "config": { + "foxy": { + "root-package-json-dir": "module/theme" + } + } +} +``` diff --git a/src/Asset/AbstractAssetManager.php b/src/Asset/AbstractAssetManager.php index 8fda97a..a445fad 100644 --- a/src/Asset/AbstractAssetManager.php +++ b/src/Asset/AbstractAssetManager.php @@ -137,6 +137,15 @@ public function run(): int return 0; } + $rootPackageDir = $this->config->get('root-package-json-dir'); + + if (is_string($rootPackageDir) && !empty($rootPackageDir)) { + if (!is_dir($rootPackageDir)) { + throw new RuntimeException(sprintf('The root package directory "%s" doesn\'t exist.', $rootPackageDir)); + } + chdir($rootPackageDir); + } + $updatable = $this->isUpdatable(); $info = sprintf('%s %s dependencies', $updatable ? 'Updating' : 'Installing', $this->getName()); $this->io->write($info); diff --git a/src/Util/AssetUtil.php b/src/Util/AssetUtil.php index 405d304..f835992 100644 --- a/src/Util/AssetUtil.php +++ b/src/Util/AssetUtil.php @@ -52,10 +52,27 @@ public static function getPath( ): string|null { $path = null; + if (self::isAsset($package, $configPackages)) { + $composerJsonPath = null; $installPath = $installationManager->getInstallPath($package); - if ($installPath !== null) { + if (null !== $installPath) { + $composerJsonPath = $installPath . '/composer.json'; + } + + if (null !== $composerJsonPath && \file_exists($composerJsonPath)) { + /** @var array[] $composerJson */ + $composerJson = \json_decode(\file_get_contents($composerJsonPath), true); + $rootPackageDir = $composerJson['config']['foxy']['root-package-json-dir'] ?? null; + + if (null !== $installPath && \is_string($rootPackageDir)) { + $installPath .= '/' . $rootPackageDir; + } + } + + + if (null !== $installPath) { $filename = $installPath . '/' . $assetManager->getPackageName(); $path = \file_exists($filename) ? \str_replace('\\', '/', \realpath($filename)) : null; } diff --git a/tests/Asset/AssetManager.php b/tests/Asset/AssetManager.php index 5d4d307..c2e756b 100644 --- a/tests/Asset/AssetManager.php +++ b/tests/Asset/AssetManager.php @@ -290,6 +290,32 @@ public function testRunForInstallCommand(int $expectedRes, string $action): void $this->assertSame('ASSET MANAGER OUTPUT', $this->executor->getLastOutput()); } + public function testSpecifyCustomDirectoryFromPackageJson(): void + { + $this->config = new Config( + [], + ['run-asset-manager' => true, 'root-package-json-dir' => $this->cwd], + ); + $this->manager = $this->getManager(); + + $this->assertSame($this->cwd, $this->config->get('root-package-json-dir')); + $this->assertSame(0, $this->getManager()->run()); + } + + public function testSpecifyCustomDirectoryFromPackageJsonException(): void + { + $this->expectException(\Foxy\Exception\RuntimeException::class); + $this->expectExceptionMessage('The root package directory "path/to/invalid" doesn\'t exist.'); + + $this->config = new Config( + [], + ['run-asset-manager' => true, 'root-package-json-dir' => 'path/to/invalid'], + ); + $this->manager = $this->getManager(); + + $this->assertSame(0, $this->getManager()->run()); + } + abstract protected function getManager(): AssetManagerInterface; abstract protected function getValidName(): string; diff --git a/tests/Fixtures/package/global/composer.json b/tests/Fixtures/package/global/composer.json index dcaf275..7f9ee8a 100644 --- a/tests/Fixtures/package/global/composer.json +++ b/tests/Fixtures/package/global/composer.json @@ -1,6 +1,7 @@ { "config": { "foxy": { + "root-package-json-dir": "theme", "global-composer-foo": 70, "global-composer-bar": 70 } diff --git a/tests/Fixtures/package/global/theme/foo/bar/package.json b/tests/Fixtures/package/global/theme/foo/bar/package.json new file mode 100644 index 0000000..2c63c08 --- /dev/null +++ b/tests/Fixtures/package/global/theme/foo/bar/package.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tests/Util/AssetUtilTest.php b/tests/Util/AssetUtilTest.php index fa14217..54f795d 100644 --- a/tests/Util/AssetUtilTest.php +++ b/tests/Util/AssetUtilTest.php @@ -18,6 +18,7 @@ use Composer\Package\PackageInterface; use Composer\Semver\Constraint\Constraint; use Foxy\Asset\AbstractAssetManager; +use Foxy\Asset\AssetManagerInterface; use Foxy\Util\AssetUtil; use PHPUnit\Framework\MockObject\MockObject; use Symfony\Component\Filesystem\Filesystem; @@ -340,4 +341,31 @@ public function testFormatPackage( $this->assertEquals($expected, $res); } + + public function testGetPathWithRootPackageDir(): void + { + $installationManager = $this->createMock(InstallationManager::class); + $installationManager + ->expects($this->once()) + ->method('getInstallPath') + ->willReturn('tests/Fixtures/package/global'); + + $assetManager = $this->createMock(AssetManagerInterface::class); + $assetManager->expects($this->once())->method('getPackageName')->willReturn('foo/bar'); + + $package = $this->createMock(PackageInterface::class); + $package->expects($this->once())->method('getName')->willReturn('foo/bar'); + $package->expects($this->once())->method('getRequires')->willReturn([]); + $package->expects($this->once())->method('getDevRequires')->willReturn([]); + + $configPackages = [ + '/^foo\/bar$/' => true, + ]; + + $expectedPath = 'tests/Fixtures/package/global/theme/foo/bar'; + + $res = AssetUtil::getPath($installationManager, $assetManager, $package, $configPackages); + + $this->assertStringContainsString($expectedPath, $res); + } }