From 4e272a9fac26f4af9cf58e1b87e43b61c439fc1c Mon Sep 17 00:00:00 2001 From: Tim Otten Date: Mon, 15 May 2023 21:33:50 -0700 Subject: [PATCH] (dev/core#4279) Add configuration option for ESM loader (a) I don't really care about customizing `esm_loader` via web UI on a day-to-day basis. It's not terrible that the web UI has the setting, but that's not the main point. So why have a setting? (b) When/if some CMS or CMS-addon causes a break in ESM loading, the site-builder should be able to swap loaders and work-around the problem. Neither loader is invicible, but they have some complementary failure-modes. Compare: - If the conflict is two browser-level "importmap"s, then switching Civi to "importmap-shim" (`esm.loader.shim`) should work-around that. - If the conflict is two copies of "es-module-shims.js", then switching to basic "importmap" (`esm.loader.browser`) should work-around that. (c) I went back/forth on whether the configuration should be a constant (`CIVICRM_ESM_LOADER`), setting (`esm_loader`), or something even more esoteric. In the event of a failure/conflict, settings provide the most media through which the site-builder can enable a work-around, eg - Edit `civicrm.settings.php`, or - Edit `/etc/civicrm.settings.d`, or - Use CLI like `cv vset` or `drush cvapi` - Use browser console like `CRM.api3` or `CRM.api4` - Use REST API (d) When/if there's a conflict, the long-term fix will (probably) be implementation of another loader (`esm.loader.drupal` or `esm.loader.wp`) based on some future API. At that point, we would need to change the default for that environment (*without changing defaults for anyone else*). Hence the support for dynamic defaults (a la `prevNextBackend` or `assetCache`). --- CRM/Admin/Form/Setting/Miscellaneous.php | 2 ++ Civi/Core/Container.php | 28 ++++++++++++++++++++++-- settings/Developer.setting.php | 19 ++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/CRM/Admin/Form/Setting/Miscellaneous.php b/CRM/Admin/Form/Setting/Miscellaneous.php index 51ddc220e6..93cdd5423f 100644 --- a/CRM/Admin/Form/Setting/Miscellaneous.php +++ b/CRM/Admin/Form/Setting/Miscellaneous.php @@ -41,6 +41,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting { 'dedupe_default_limit' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'remote_profile_submissions' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'allow_alert_autodismissal' => CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, + 'esm_loader' => CRM_Core_BAO_Setting::DEVELOPER_PREFERENCES_NAME, 'prevNextBackend' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, 'import_batch_size' => CRM_Core_BAO_Setting::SEARCH_PREFERENCES_NAME, ]; @@ -62,6 +63,7 @@ class CRM_Admin_Form_Setting_Miscellaneous extends CRM_Admin_Form_Setting { 'recentItemsMaxCount', 'recentItemsProviders', 'dedupe_default_limit', + 'esm_loader', 'prevNextBackend', 'import_batch_size', ]); diff --git a/Civi/Core/Container.php b/Civi/Core/Container.php index e92ab7ebf0..82768108dd 100644 --- a/Civi/Core/Container.php +++ b/Civi/Core/Container.php @@ -5,6 +5,7 @@ use Civi\Core\Compiler\AutoServiceScannerPass; use Civi\Core\Compiler\EventScannerPass; use Civi\Core\Compiler\SpecProviderPass; use Civi\Core\Event\EventScanner; +use Civi\Core\Event\GenericHookEvent; use Civi\Core\Lock\LockManager; use Symfony\Component\Config\ConfigCache; use Symfony\Component\DependencyInjection\Compiler\PassConfig; @@ -392,8 +393,10 @@ class Container { [new Reference('action_object_provider')] ); - // $container->setAlias('esm.loader', 'esm.loader.browser')->setPublic(TRUE); - $container->setAlias('esm.loader', 'esm.loader.shim')->setPublic(TRUE); + $container->setDefinition('esm.loader', new Definition( + 'object', + [new Reference('service_container')] + ))->setFactory([new Reference(self::SELF), 'createEsmLoader'])->setPublic(TRUE); \CRM_Utils_Hook::container($container); @@ -577,6 +580,27 @@ class Container { return $container->get('prevnext.driver.' . $setting); } + /** + * Determine which component will load ECMAScript Modules. + * + * @param \Symfony\Component\DependencyInjection\ContainerInterface $container + * @return \object + */ + public static function createEsmLoader($container): object { + $name = \Civi::settings()->get('esm_loader'); + if ($name === 'auto') { + $name = 'shim'; + \Civi::dispatcher()->dispatch('civi.esm.loader.default', GenericHookEvent::create(['default' => &$name])); + } + if ($container->has("esm.loader.$name")) { + return $container->get("esm.loader.$name"); + } + else { + \Civi::log()->warning('Invalid ESM loader: {name}', ['name' => $name]); + return $container->get("esm.loader.browser"); + } + } + /** * @return \ArrayObject */ diff --git a/settings/Developer.setting.php b/settings/Developer.setting.php index ced086397d..8dea989ea6 100644 --- a/settings/Developer.setting.php +++ b/settings/Developer.setting.php @@ -103,6 +103,25 @@ return [ 'CRM_Core_BAO_Setting::onChangeEnvironmentSetting', ], ], + 'esm_loader' => [ + 'group_name' => 'Developer Preferences', + 'group' => 'developer', + 'name' => 'esm_loader', + 'type' => 'String', + 'quick_form_type' => 'Select', + 'html_type' => 'Select', + 'html_attributes' => [ + //'class' => 'crm-select2', + ], + 'default' => 'auto', + 'add' => '5.63', + 'title' => ts('ECMAScript Module Loader'), + 'is_domain' => 1, + 'is_contact' => 0, + 'description' => NULL, + 'help_text' => NULL, + 'options' => ['auto' => ts('Default (Auto-detect)'), 'browser' => ts('Browser'), 'shim' => ts('es-module-shims')], + ], 'fatalErrorHandler' => [ 'group_name' => 'Developer Preferences', 'group' => 'developer', -- 2.25.1