Loading cl_components_examples/templates/components/my-card/metadata.json +4 −3 Original line number Diff line number Diff line Loading @@ -4,13 +4,14 @@ "name": "Card", "status": "BETA", "componentType": "organism", "variants": ["light"], "variants": [ "light" ], "schemas": { "props": { "type": "object", "required": [ "header", "children" "header" ], "properties": { "header": { Loading src/Component/ComponentDiscovery.php +49 −33 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class ComponentDiscovery { private static $directoryIteratorFlags = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS; /** * Cached component information. * Cached component information keyed by component ID. * * @var \Drupal\cl_components\Component\Component[]|null */ Loading Loading @@ -75,14 +75,16 @@ class ComponentDiscovery { $components = $this->findAll(); $filtered = array_filter( $components, fn(Component $component) => $component->getMetadata()->getComponentType() === ComponentMetadata::COMPONENT_TYPE_ORGANISM fn(Component $component) => $component->getMetadata() ->getComponentType() === ComponentMetadata::COMPONENT_TYPE_ORGANISM ); if (!$no_wip) { return $filtered; } return array_filter( $filtered, fn(Component $component) => $component->getMetadata()->getStatus() !== ComponentMetadata::COMPONENT_STATUS_WIP fn(Component $component) => $component->getMetadata() ->getStatus() !== ComponentMetadata::COMPONENT_STATUS_WIP ); } Loading @@ -94,47 +96,49 @@ class ComponentDiscovery { */ public function findAll(): array { if (isset($this->components)) { return $this->components; return array_values($this->components); } $components_by_key = fn(array $data) => array_reduce( $data, static fn(array $all, Component $c) => [...$all, $c->getId() => $c], [] ); $cache_ids = $this->cache->get('all-cache-ids'); if ($cache_ids && is_array($cache_ids->data)) { $cache_entries = $this->cache->getMultiple($cache_ids->data); $components = array_map( // Get the data of the cache properties. $cache_data = array_map( fn(object $item) => $item->data, $cache_entries ); $this->components = array_values($components); return $this->components; } $unflattened = array_map(function (string $path) { try { $directory_iterator = new \RecursiveDirectoryIterator($path, static::$directoryIteratorFlags); } catch (\UnexpectedValueException $exception) { watchdog_exception('cl_components', $exception); return []; } $components = array_map( [$this, 'createComponent'], $this->discoverComponentPaths($directory_iterator, []) // Ensure cache data deserializes to a Component. $cached_components = array_filter( $cache_data, static fn(mixed $item) => $item instanceof Component ); return array_filter($components); }, $this->scanDirs); $this->components = $components_by_key($cached_components); return array_values($this->components); } $paths = $this->findAllPaths(); $this->components = array_filter(array_map([$this, 'createComponent'], $paths)); $components = array_map([$this, 'createComponent'], $paths); $components = array_filter($components); $this->components = $components_by_key($components); $this->cache->set( 'all-cache-ids', array_map(static fn(Component $component) => 'component::' . $component->getId(), $this->components) array_map( static fn(Component $component) => 'component::' . $component->getId(), $this->components ) ); $this->cache->setMultiple(array_reduce( $this->components, $components, static fn(array $items, Component $component) => array_merge( $items, ['component::' . $component->getId() => ['data' => $component]] ), [] )); return $this->components; return array_values($this->components); } /** Loading @@ -158,6 +162,7 @@ class ComponentDiscovery { }, $this->scanDirs); return array_merge(...$unflattened); } /** * Returns all the components in the repository pointed by the iterator. * Loading Loading @@ -297,20 +302,28 @@ class ComponentDiscovery { * When the component cannot be found. */ public function find(string $id): Component { // Check if the component is in memory cache. if (($this->components[$id] ?? NULL) instanceof Component) { return $this->components[$id]; } // Check if the component is in persisted cache. $cached = $this->cache->get('component::' . $id); if ($cached && $cached->data instanceof Component) { return $cached->data; } // Find all components and search for ours. $components = $this->findAll(); $matches = array_filter( $components, static function (Component $component) use ($id) { return $component->getId() === $id; } static fn(Component $c) => $c->getId() === $id ); $component = reset($matches); if (!$component instanceof Component) { if ($component instanceof Component) { return $component; } $message = sprintf('Unable to find component "%s" in the component repository', $id); throw new ComponentNotFoundException($message); } return $component; } /** * Gets the directories to scan for components. Loading @@ -336,7 +349,10 @@ class ComponentDiscovery { return $this->instantiateComponent($path); } catch (InvalidComponentException $e) { watchdog_exception('cl_components', $e, 'Invalid component @path. Error: @error', ['@path' => $path, '@error' => $e->getMessage()]); watchdog_exception('cl_components', $e, 'Invalid component @path. Error: @error', [ '@path' => $path, '@error' => $e->getMessage(), ]); return NULL; } } Loading src/Component/ComponentMetadata.php +4 −9 Original line number Diff line number Diff line Loading @@ -204,15 +204,13 @@ final class ComponentMetadata { * @throws \Drupal\cl_components\Exception\InvalidComponentException */ private function parseSchemaInfo(array $metadata_info): void { $default_props_schema = [ $default_schema = [ 'type' => 'object', 'additionalProperties' => FALSE, 'required' => [], 'properties' => [ 'children' => ['type' => 'string'], ], 'properties' => [], ]; $this->schemas = $metadata_info['schemas'] ?? ['props' => $default_props_schema]; $this->schemas = $metadata_info['schemas'] ?? ['props' => $default_schema]; if (($this->schemas['props']['type'] ?? 'object') !== 'object') { throw new InvalidComponentException('The schema for the props in the component metadata is invalid. The schema should be of type "object".'); } Loading @@ -220,12 +218,9 @@ final class ComponentMetadata { throw new InvalidComponentException('The schema for the props in the component metadata is invalid. Arbitrary additional properties are not allowed.'); } $this->schemas['props']['additionalProperties'] = FALSE; $this->schemas['props']['properties']['children'] = ['type' => 'string']; // Save the props. $schema_props = $metadata_info['schemas']['props'] ?? $default_props_schema; $required_info = $schema_props['required'] ?? []; $schema_props = $metadata_info['schemas']['props'] ?? $default_schema; foreach ($schema_props['properties'] ?? [] as $name => $schema) { $is_required = in_array($name, $required_info); // All props should also support "object" this allows deferring rendering // in Twig to the render pipeline. $type = $schema['type'] ?? ''; Loading Loading
cl_components_examples/templates/components/my-card/metadata.json +4 −3 Original line number Diff line number Diff line Loading @@ -4,13 +4,14 @@ "name": "Card", "status": "BETA", "componentType": "organism", "variants": ["light"], "variants": [ "light" ], "schemas": { "props": { "type": "object", "required": [ "header", "children" "header" ], "properties": { "header": { Loading
src/Component/ComponentDiscovery.php +49 −33 Original line number Diff line number Diff line Loading @@ -24,7 +24,7 @@ class ComponentDiscovery { private static $directoryIteratorFlags = \FilesystemIterator::KEY_AS_PATHNAME | \FilesystemIterator::CURRENT_AS_SELF | \FilesystemIterator::SKIP_DOTS; /** * Cached component information. * Cached component information keyed by component ID. * * @var \Drupal\cl_components\Component\Component[]|null */ Loading Loading @@ -75,14 +75,16 @@ class ComponentDiscovery { $components = $this->findAll(); $filtered = array_filter( $components, fn(Component $component) => $component->getMetadata()->getComponentType() === ComponentMetadata::COMPONENT_TYPE_ORGANISM fn(Component $component) => $component->getMetadata() ->getComponentType() === ComponentMetadata::COMPONENT_TYPE_ORGANISM ); if (!$no_wip) { return $filtered; } return array_filter( $filtered, fn(Component $component) => $component->getMetadata()->getStatus() !== ComponentMetadata::COMPONENT_STATUS_WIP fn(Component $component) => $component->getMetadata() ->getStatus() !== ComponentMetadata::COMPONENT_STATUS_WIP ); } Loading @@ -94,47 +96,49 @@ class ComponentDiscovery { */ public function findAll(): array { if (isset($this->components)) { return $this->components; return array_values($this->components); } $components_by_key = fn(array $data) => array_reduce( $data, static fn(array $all, Component $c) => [...$all, $c->getId() => $c], [] ); $cache_ids = $this->cache->get('all-cache-ids'); if ($cache_ids && is_array($cache_ids->data)) { $cache_entries = $this->cache->getMultiple($cache_ids->data); $components = array_map( // Get the data of the cache properties. $cache_data = array_map( fn(object $item) => $item->data, $cache_entries ); $this->components = array_values($components); return $this->components; } $unflattened = array_map(function (string $path) { try { $directory_iterator = new \RecursiveDirectoryIterator($path, static::$directoryIteratorFlags); } catch (\UnexpectedValueException $exception) { watchdog_exception('cl_components', $exception); return []; } $components = array_map( [$this, 'createComponent'], $this->discoverComponentPaths($directory_iterator, []) // Ensure cache data deserializes to a Component. $cached_components = array_filter( $cache_data, static fn(mixed $item) => $item instanceof Component ); return array_filter($components); }, $this->scanDirs); $this->components = $components_by_key($cached_components); return array_values($this->components); } $paths = $this->findAllPaths(); $this->components = array_filter(array_map([$this, 'createComponent'], $paths)); $components = array_map([$this, 'createComponent'], $paths); $components = array_filter($components); $this->components = $components_by_key($components); $this->cache->set( 'all-cache-ids', array_map(static fn(Component $component) => 'component::' . $component->getId(), $this->components) array_map( static fn(Component $component) => 'component::' . $component->getId(), $this->components ) ); $this->cache->setMultiple(array_reduce( $this->components, $components, static fn(array $items, Component $component) => array_merge( $items, ['component::' . $component->getId() => ['data' => $component]] ), [] )); return $this->components; return array_values($this->components); } /** Loading @@ -158,6 +162,7 @@ class ComponentDiscovery { }, $this->scanDirs); return array_merge(...$unflattened); } /** * Returns all the components in the repository pointed by the iterator. * Loading Loading @@ -297,20 +302,28 @@ class ComponentDiscovery { * When the component cannot be found. */ public function find(string $id): Component { // Check if the component is in memory cache. if (($this->components[$id] ?? NULL) instanceof Component) { return $this->components[$id]; } // Check if the component is in persisted cache. $cached = $this->cache->get('component::' . $id); if ($cached && $cached->data instanceof Component) { return $cached->data; } // Find all components and search for ours. $components = $this->findAll(); $matches = array_filter( $components, static function (Component $component) use ($id) { return $component->getId() === $id; } static fn(Component $c) => $c->getId() === $id ); $component = reset($matches); if (!$component instanceof Component) { if ($component instanceof Component) { return $component; } $message = sprintf('Unable to find component "%s" in the component repository', $id); throw new ComponentNotFoundException($message); } return $component; } /** * Gets the directories to scan for components. Loading @@ -336,7 +349,10 @@ class ComponentDiscovery { return $this->instantiateComponent($path); } catch (InvalidComponentException $e) { watchdog_exception('cl_components', $e, 'Invalid component @path. Error: @error', ['@path' => $path, '@error' => $e->getMessage()]); watchdog_exception('cl_components', $e, 'Invalid component @path. Error: @error', [ '@path' => $path, '@error' => $e->getMessage(), ]); return NULL; } } Loading
src/Component/ComponentMetadata.php +4 −9 Original line number Diff line number Diff line Loading @@ -204,15 +204,13 @@ final class ComponentMetadata { * @throws \Drupal\cl_components\Exception\InvalidComponentException */ private function parseSchemaInfo(array $metadata_info): void { $default_props_schema = [ $default_schema = [ 'type' => 'object', 'additionalProperties' => FALSE, 'required' => [], 'properties' => [ 'children' => ['type' => 'string'], ], 'properties' => [], ]; $this->schemas = $metadata_info['schemas'] ?? ['props' => $default_props_schema]; $this->schemas = $metadata_info['schemas'] ?? ['props' => $default_schema]; if (($this->schemas['props']['type'] ?? 'object') !== 'object') { throw new InvalidComponentException('The schema for the props in the component metadata is invalid. The schema should be of type "object".'); } Loading @@ -220,12 +218,9 @@ final class ComponentMetadata { throw new InvalidComponentException('The schema for the props in the component metadata is invalid. Arbitrary additional properties are not allowed.'); } $this->schemas['props']['additionalProperties'] = FALSE; $this->schemas['props']['properties']['children'] = ['type' => 'string']; // Save the props. $schema_props = $metadata_info['schemas']['props'] ?? $default_props_schema; $required_info = $schema_props['required'] ?? []; $schema_props = $metadata_info['schemas']['props'] ?? $default_schema; foreach ($schema_props['properties'] ?? [] as $name => $schema) { $is_required = in_array($name, $required_info); // All props should also support "object" this allows deferring rendering // in Twig to the render pipeline. $type = $schema['type'] ?? ''; Loading