Skip to content

Commit e395487

Browse files
committed
Add the ability to rename files/folders and support vendor namespaces.
Also adds a convenience "install-drupal-libraries" command.
1 parent 723eca1 commit e395487

22 files changed

+498
-25
lines changed

CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
1.3.0 / 2020-07-27
2+
========================
3+
* Add a `rename` property to the library definition, providing the ability to
4+
rename a library asset to match a particular folder pattern.
5+
* Add support for libraries with vendor namespaces like [ckeditor][ckeditor-downloads].
6+
* Add a convenience `install-drupal-libraries` composer command. It typically
7+
requires your composer dependencies to have already been resolved.
8+
19
1.2.0 / 2020-07-20
210
========================
311
* Address a `LogicException` being thrown when the package is uninstalled.
@@ -25,3 +33,5 @@ definition for supporting:
2533
1.0.0 / 2018-01-25
2634
========================
2735
* Initial MVP plugin.
36+
37+
[ckeditor-downloads]: https://github.com/balbuf/drupal-libraries-installer/issues/6

README.md

+45-5
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,15 @@ If the file is not a ZIP file, then the URL must end with the file extension:
5757
"*.{md}",
5858
"Gruntfile.js",
5959
"{docs,src,tests}"
60-
]
60+
],
61+
"rename": {
62+
"dist": "build"
63+
}
6164
},
6265
"custom-tar-asset": {
63-
"url": "https://assets.custom-url.com/unconventional/url/path",
64-
"type": "tar",
65-
"ignore": [".*", "*.{txt,md}"]
66+
"url": "https://assets.custom-url.com/unconventional/url/path",
67+
"type": "tar",
68+
"ignore": [".*", "*.{txt,md}"]
6669
}
6770
}
6871
}
@@ -74,6 +77,7 @@ If the file is not a ZIP file, then the URL must end with the file extension:
7477
- `version`: The version of the library (defaults to `1.0.0`).
7578
- `type`: The type of library archive, one of (zip, tar, rar, gzip), support depends on your composer version (defaults to `zip`).
7679
- `ignore`: Array of folders/file globs to remove from the library (defaults to `[]`). See [PSA-2011-002](https://www.drupal.org/node/1189632).
80+
- `rename`: Object mapping of folders/files to rename to fit a certain folder structure (optional).
7781
- `shasum`: The SHA1 hash of the asset (optional).
7882
7983
_See below for how to find the ZIP URL for a GitHub repo._
@@ -170,12 +174,48 @@ desired link to use within your `composer.json` file.
170174
If the library does not provide any releases, you can still reference it in ZIP file form.
171175
The downside is that any time you download this ZIP, the contents may change based on the
172176
state of the repo. There is no guarantee that separate users of the project will have the
173-
exact same version of the library.
177+
exact same version of the library. To mitigate against this issue, you should
178+
always download a specific commit version rather than from a branch like `master`.
174179

175180
1. Click the green `Clone or download` button on the repo's home page.
176181
177182
1. Copy the URL for the `Download ZIP` link to use within your `composer.json` file.
178183
184+
### Library definitions with namespaces
185+
186+
If a library includes a vendor namespace, then its internal package name
187+
will be prefixed with a `drupal-library_` e.g. `vendor/library` becomes
188+
`drupal-library_vendor/library`, which in turn allows you to add a
189+
custom installer option like the following to manage where it's downloaded:
190+
```json5
191+
{
192+
// composer.json
193+
"extra": {
194+
"installer-paths": {
195+
// Custom installer path entry to store them all under the same folder.
196+
"web/libraries/myvendor/{$name}": [
197+
"vendor:drupal-library_ckeditor"
198+
],
199+
"web/libraries/{$name}/": [
200+
"type:drupal-library"
201+
]
202+
},
203+
"drupal-libraries": {
204+
"myvendor/package1": "https://download.myvendor.com/package1-1.0.0.zip",
205+
"myvendor/package2": "https://download.myvendor.com/package2-1.4.0.zip"
206+
}
207+
},
208+
}
209+
```
210+
211+
Otherwise the files will be stored in `web/libraries/myvendor` by default and
212+
will overwrite each other.
213+
214+
**NB**: The order of the `installer-paths` matters.
215+
216+
The justification behind the prefix is to avoid any potential collision with
217+
normal composer packages.
218+
179219
## Notes
180220

181221
- This plugin is essentially a shortcut for explicitly declaring the composer package

example/project/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
# Demo root project
22

33
Demo root project integrating with Drupal libraries installer.
4+
5+
Update locally after committing by running `composer update zodiacmedia/drupal-libraries-installer`.

example/project/composer.json

+13-3
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
},
3535
"extra": {
3636
"installer-paths": {
37+
"web/libraries/ckeditor/{$name}": [
38+
"vendor:drupal-library_ckeditor"
39+
],
3740
"web/libraries/{$name}/": [
3841
"type:drupal-library"
3942
]
@@ -42,6 +45,8 @@
4245
"zodiacmedia/drupal-libraries-installer-demo-dependency"
4346
],
4447
"drupal-libraries": {
48+
"ckeditor/codesnippet": "https://download.ckeditor.com/codesnippet/releases/codesnippet_4.9.2.zip",
49+
"ckeditor/contents": "https://download.ckeditor.com/contents/releases/contents_0.11.zip",
4550
"simple-color": "https://github.com/recurser/jquery-simple-color/archive/v1.2.2.zip",
4651
"chosen": {
4752
"url": "https://github.com/harvesthq/chosen/releases/download/v1.8.2/chosen_v1.8.2.zip",
@@ -65,10 +70,15 @@
6570
"url": "https://github.com/select2/select2/archive/4.0.13.zip",
6671
"ignore": [
6772
".*",
68-
"*.{md}",
69-
"Gruntfile.js",
7073
"{docs,src,tests}"
71-
]
74+
],
75+
"rename": {
76+
"dist": "build",
77+
"empty_directory": "../../../../../../../../moved-outside-library-directory",
78+
"LICENSE.md": "README.md",
79+
"select2.js": "index.js",
80+
"non-existent": "ignored"
81+
}
7282
}
7383
}
7484
}

src/InstallLibrariesEvent.php

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Zodiacmedia\DrupalLibrariesInstaller;
4+
5+
use Composer\Script\Event;
6+
7+
/**
8+
* The Install Drupal libraries event.
9+
*/
10+
class InstallLibrariesEvent extends Event {
11+
12+
/**
13+
* The event is triggered when the 'install-drupal-libraries' command is ran.
14+
*
15+
* The event listener method receives a
16+
* \Zodiacmedia\DrupalLibrariesInstaller\DownloadLibraryEvent instance.
17+
*
18+
* @var string
19+
*/
20+
const INSTALL_LIBRARIES = 'install-drupal-libraries';
21+
22+
}

src/Plugin.php

+60-7
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,26 @@
99
use Composer\Package\CompletePackage;
1010
use Composer\Package\Package;
1111
use Composer\Package\PackageInterface;
12+
use Composer\Plugin\Capable;
1213
use Composer\Plugin\PluginInterface;
1314
use Composer\Script\Event;
1415
use Composer\Script\ScriptEvents;
1516
use Composer\Util\Filesystem;
1617
use Symfony\Component\Finder\Finder;
1718
use Symfony\Component\Finder\Glob;
19+
use Composer\Plugin\Capability\CommandProvider;
1820

1921
/**
2022
* The Drupal libraries installer plugin.
2123
*/
22-
class Plugin implements PluginInterface, EventSubscriberInterface {
24+
class Plugin implements PluginInterface, Capable, EventSubscriberInterface {
2325

2426
/**
2527
* The installed-libraries.json lock file schema version.
2628
*
2729
* @var string
2830
*/
29-
const SCHEMA_VERSION = '1.0';
31+
const SCHEMA_VERSION = '1.1';
3032

3133
/**
3234
* The composer package name.
@@ -94,14 +96,24 @@ public static function getSubscribedEvents() {
9496
return [
9597
ScriptEvents::POST_INSTALL_CMD => 'install',
9698
ScriptEvents::POST_UPDATE_CMD => 'install',
99+
InstallLibrariesEvent::INSTALL_LIBRARIES => 'install',
100+
];
101+
}
102+
103+
/**
104+
* {@inheritDoc}
105+
*/
106+
public function getCapabilities() {
107+
return [
108+
CommandProvider::class => PluginCommandProvider::class,
97109
];
98110
}
99111

100112
/**
101113
* Upon running composer install or update, install the drupal libraries.
102114
*
103-
* @param \Composer\Script\Event $event
104-
* The composer install/update event.
115+
* @param \Composer\Script\Event|\Zodiacmedia\DrupalLibrariesInstaller\InstallLibrariesEvent $event
116+
* The composer install/update/install-drupal-libraries event.
105117
*
106118
* @throws \Exception
107119
*/
@@ -209,6 +221,7 @@ protected function processPackage(array $processed_drupal_libraries, array $drup
209221
// Install each library.
210222
foreach ($extra['drupal-libraries'] as $library => $library_definition) {
211223
$ignore_patterns = [];
224+
$rename = [];
212225
$sha1checksum = NULL;
213226
if (is_string($library_definition)) {
214227
// Simple format.
@@ -224,6 +237,7 @@ protected function processPackage(array $processed_drupal_libraries, array $drup
224237
$version = $library_definition['version'] ?? $version;
225238
$distribution_type = $library_definition['type'] ?? $distribution_type;
226239
$ignore_patterns = $library_definition['ignore'] ?? $ignore_patterns;
240+
$rename = $library_definition['rename'] ?? $rename;
227241
$sha1checksum = $library_definition['shasum'] ?? $sha1checksum;
228242
}
229243

@@ -251,8 +265,12 @@ protected function processPackage(array $processed_drupal_libraries, array $drup
251265
'url' => $url,
252266
'type' => $distribution_type,
253267
'ignore' => $ignore_patterns,
268+
'rename' => $rename,
254269
'package' => $package->getName(),
255270
];
271+
if (empty($rename)) {
272+
unset($applied_library['rename']);
273+
}
256274
if (isset($sha1checksum)) {
257275
$applied_library['shasum'] = $sha1checksum;
258276
}
@@ -294,6 +312,7 @@ protected function downloadLibraries(array $processed_libraries, array $applied_
294312
$library_package = $this->getLibraryPackage($library_name, $processed_library);
295313

296314
$ignore_patterns = $processed_library['ignore'];
315+
$rename = $processed_library['rename'] ?? NULL;
297316
$install_path = $this->installationManager->getInstallPath($library_package);
298317
if (
299318
(
@@ -306,7 +325,7 @@ protected function downloadLibraries(array $processed_libraries, array $applied_
306325
// - wasn't in the lock file.
307326
// - doesn't match what is in the lock file.
308327
// - doesn't exist on disk.
309-
$this->downloadPackage($library_package, $install_path, $ignore_patterns);
328+
$this->downloadPackage($library_package, $install_path, $ignore_patterns, $rename);
310329
}
311330
}
312331
}
@@ -320,8 +339,10 @@ protected function downloadLibraries(array $processed_libraries, array $applied_
320339
* The package install path.
321340
* @param array $ignore_patterns
322341
* File patterns to ignore.
342+
* @param array|null $rename
343+
* Array mapping of files/folders to rename.
323344
*/
324-
protected function downloadPackage(Package $library_package, $install_path, array $ignore_patterns) {
345+
protected function downloadPackage(Package $library_package, $install_path, array $ignore_patterns, array $rename = NULL) {
325346
// Let composer download and unpack the library for us!
326347
$this->downloadManager->download($library_package, $install_path);
327348

@@ -364,6 +385,30 @@ function ($file) use ($patterns) {
364385
$this->fileSystem->remove($file_pathname);
365386
}
366387
}
388+
389+
if ($rename) {
390+
foreach ($rename as $original_file => $destination_file) {
391+
$original_file = $this->fileSystem->normalizePath("$install_path/$original_file");
392+
$destination_file = $this->fileSystem->normalizePath("$install_path/$destination_file");
393+
if (strpos($original_file, $install_path) !== 0) {
394+
$this->io->writeError(" - Could not rename <info>$original_file</info> as it is outside the library directory.");
395+
}
396+
elseif (strpos($destination_file, $install_path) !== 0) {
397+
$this->io->writeError(" - Could not rename <info>$destination_file</info> as it is outside the library directory.");
398+
}
399+
elseif (!file_exists($original_file)) {
400+
$this->io->writeError(" - Could not rename <info>$original_file</info> as it does not exist");
401+
}
402+
elseif (file_exists($destination_file)) {
403+
$this->io->writeError(" - Could not rename <info>$original_file</info> as the destination file <info>$destination_file</info> already exists");
404+
}
405+
else {
406+
$this->io->writeError(" - Renaming <info>$original_file</info> to <info>$destination_file</info>");
407+
// Attempt to move the file over.
408+
$this->fileSystem->rename($original_file, $destination_file);
409+
}
410+
}
411+
}
367412
}
368413

369414
/**
@@ -378,7 +423,15 @@ function ($file) use ($patterns) {
378423
* The pseudo-package for the library.
379424
*/
380425
protected function getLibraryPackage($library_name, array $library_definition) {
381-
$library_package_name = 'drupal-library/' . $library_name;
426+
if (strpos($library_name, '/')) {
427+
// The library name already contains a '/', add the "drupal-library_"
428+
// prefix to it so that it can be configured to a custom path through its
429+
// vendor name.
430+
$library_package_name = "drupal-library_$library_name";
431+
}
432+
else {
433+
$library_package_name = 'drupal-library/' . $library_name;
434+
}
382435
$library_package = new Package(
383436
$library_package_name, $library_definition['version'], $library_definition['version']
384437
);

src/PluginCommand.php

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php
2+
3+
namespace Zodiacmedia\DrupalLibrariesInstaller;
4+
5+
use Composer\Command\BaseCommand;
6+
use Symfony\Component\Console\Input\InputInterface;
7+
use Symfony\Component\Console\Output\OutputInterface;
8+
9+
/**
10+
* The composer plugin command provider.
11+
*/
12+
class PluginCommand extends BaseCommand {
13+
14+
/**
15+
* {@inheritDoc}
16+
*/
17+
protected function configure() {
18+
$this->setName('install-drupal-libraries');
19+
$this->setDescription(
20+
'Download and install the drupal libraries.'
21+
);
22+
}
23+
24+
/**
25+
* {@inheritDoc}
26+
*/
27+
protected function execute(InputInterface $input, OutputInterface $output) {
28+
$composer = $this->getComposer();
29+
30+
$install_libraries_event = new InstallLibrariesEvent(
31+
InstallLibrariesEvent::INSTALL_LIBRARIES,
32+
$composer,
33+
$this->getIO(),
34+
FALSE
35+
);
36+
return $composer->getEventDispatcher()->dispatch(
37+
$install_libraries_event->getName(),
38+
$install_libraries_event
39+
);
40+
}
41+
42+
}

src/PluginCommandProvider.php

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<?php
2+
3+
namespace Zodiacmedia\DrupalLibrariesInstaller;
4+
5+
use Composer\Plugin\Capability\CommandProvider;
6+
7+
/**
8+
* The composer plugin command provider.
9+
*/
10+
class PluginCommandProvider implements CommandProvider {
11+
12+
/**
13+
* {@inheritDoc}
14+
*/
15+
public function getCommands() {
16+
return [
17+
new PluginCommand(),
18+
];
19+
}
20+
21+
}

0 commit comments

Comments
 (0)