A Composer plugin that "publishes" assets for packages where WordPress can find them.
When building Composer-based "whole-websites" we might require via Composer packages containing WordPress code without them being WP plugins or themes.
Such packages would be placed in the vendor
folder which is oftentimes outside webroot.
And that means that if such packages have assets such as images, scripts, or styles, the browser will not be able to reach them.
That forces us to either write regular WP plugins or move the vendor
folder inside the webroot.
Neither of the above solutions is ideal, even if both might work depending on the use case.
This package provides an alternative: it symlinks (or copies) such assets files under
wp-content/plugins
folder, that is reachable by the browser.
Moreover, being that a WP standard path, we can use functions like plugin_url()
to get the URL.
This is a Composer plugin that provides a custom installer for packages having
"wordpress-package"
as type. Additional types can be supported via configuration.
The installer does not change how the default Composer installer installs the package, but
after the default installer has successfully installed the package, it looks into an
extra.package-assets-paths
property in the package's composer.json
and symlinks all the
paths found there into the /wp-content/plugins/.published-package-assets/{vendor}/{name}
folder.
The wp-content
base folder is determined based on configuration on root package.
When using WP Starter, its
wordpress-content-dir
setting
suffices as configuration.
Of course, the extra.wordpress-content-dir
property can be used even if WP Starter is not in use.
Let's assume a package with a composer.json
like the following:
{
"name": "acme/awesome-package",
"type": "wordpress-package",
"require": {
"wecodemore/wp-package-assets-publisher": "@dev"
},
"autoload": {
"files": [
"bootstrap.php"
]
},
"extra": {
"package-assets": [
"./public"
]
}
}
Let's now assume the files structure of the package is the following:
|⎯ 📄bootstrap.php
|⎯ 📄composer.json
|⎯ 📂 public
|⎯ 📄main.css
|⎯ 📄main.js
Now, suppose the above package is required in a WP Starter project with the following composer.json
:
{
"name": "acme/acme-website",
"type": "project",
"require": {
"wecodemore/wpstarter": "^3@dev",
"roots/wordpress": "6.1.*",
"acme/awesome-package": "^1.0"
},
"config": {
"allow-plugins": {
"composer/*": true,
"wecodemore/*": true,
"roots/wordpress-core-installer": true
}
},
"extra": {
"wordpress-install-dir": "./public/wp",
"wordpress-content-dir": "./public/wp-content",
"installer-paths": {
"./public/wp-content/plugins/{$name}/": ["type:wordpress-plugin"],
"./public/wp-content/themes/{$name}/": ["type:wordpress-theme"],
"./public/wp-content/mu-plugins/{$name}/": ["type:wordpress-muplugin"]
}
}
}
After Composer install, we will find a folder structure like this (showing only the relevant bits):
|⎯ 📂 public
| |⎯ 📂 wp-content
| |⎯ 📂 plugins
| |⎯ 📂.published-package-assets
| |⎯ 📂 acme
| |⎯ 📂 awesome-package
| |⎯ 📂 public
| |⎯ 📄main.css
| |⎯ 📄main.js
|⎯ 📂 vendor
|⎯ 📂 acme
|⎯ 📂 awesome-package
|⎯ 📄bootstrap.php
|⎯ 📄composer.json
|⎯ 📂 public
|⎯ 📄main.css
|⎯ 📄main.js
So the package is regularly installed in vendor/
, but its public/
folder, that was listed in the
package's extra.package-assets-paths
property, have been symlinked in
public/wp-content/.published-package-assets/acme/awesome-package/public/
.
Thanks to that, the package assets are now under webroot, and so reachable by the browser.
Getting the full URL of that package's assets can be hard, though. This is why this package ships
a helper function WeCodeMore\packageAssetUrl()
which can be used anywhere like so:
$url = WeCodeMore\packageAssetUrl('acme/awesome-package', 'public/main.js');
The function takes two arguments, the first is the name of the package as defined in its
composer.json
, and the second is the relative path of the asset to retrieve.
Of course, it works only if the requested path was configured in the package's
extra.package-assets-paths
property, and so "published" in place by this package.
In projects not using WP Starter, the extra.wordpress-content-dir
property can still be used to
tell the assets published where to place th published assets.
Alternatively, the root-only extra.package-assets-publisher.publish-dir
config can be used
instead.
Here's an example of using the package with the Bedrock's folder structure:
{
"extra": {
"package-assets-publisher": {
"publish-dir": "./web/app/plugins"
}
}
}
The package by default tries to symlink assets paths and only resorts to copy them if symlink fails, similarly to how Composer does.
To force the package to copy the files, it is possible to use a different format for the
extra.package-assets-publisher.symlink
property, like the following:
{
"extra": {
"package-assets-publisher": {
"symlink": false
}
}
}
To force the package to symlink the files and not attempt a copy, set
extra.package-assets-publisher.symlink
set to true
.
Setting extra.package-assets-publisher.symlink
to any non-boolean value, or not setting it at all
results in the default behavior that symlink are attempted with copy fallback.
By default, when a failure happens, the package prints an error message, but does not break the Composer installation/update process.
To fail "hard", set extra.package-assets-publisher.strict
to true
.
{
"extra": {
"package-assets-publisher": {
"strict": true
}
}
}
The installer by default only attempts to publish assets for packages with type: "wordpress-package". It is possible to support additional types, via configuration, like this:
{
"extra": {
"package-assets-publisher": {
"types": [
"wp-library"
]
}
}
}
Note: This package is an installer for custom types. By declaring support for a type we will override the default installer for that type, which means that if a package type needs to use a custom installer, it should not be added to the supported types for this package.
Both the extra.package-assets-publisher.symlink
and the extra.package-assets-publisher.strict
root options, can be overridden at the package level.
That is done via the package-level extra.package-assets
property, using it as an object like this:
{
"extra": {
"package-assets": {
"paths": [
"./public"
],
"options": {
"symlink": false,
"strict": true
}
}
}
}
- PHP 7.4+
- Composer >= 2.3
MIT. See LICENSE file.