Skip to content

Replace iconify/json with build-time icon fetching#499

Open
titouanmathis wants to merge 11 commits intodevelopfrom
feature/498-build-time-icon-fetching
Open

Replace iconify/json with build-time icon fetching#499
titouanmathis wants to merge 11 commits intodevelopfrom
feature/498-build-time-icon-fetching

Conversation

@titouanmathis
Copy link
Contributor

Summary

Closes #498

Replaces the ~690MB iconify/json runtime dependency with a Composer plugin that fetches only the icons actually used in templates from the Iconify API at build-time.

What changed

New: Composer plugin (packages/twig-extension/Composer/)

  • Plugin — hooks into composer install / composer update to auto-sync icons
  • Config — reads settings from composer.json extra.studiometa/ui.icons
  • IconScanner — scans template directories for meta_icon() calls
  • IconFetcher — batch-fetches icons from the Iconify REST API
  • IconStorage — manages local SVG files ({output}/{prefix}/{name}.svg)

CLI commands

  • composer ui:icons — scan & sync icons
  • composer ui:icons --dry-run — show detected icons without fetching
  • composer ui:icons --prune — sync + remove unused icons
  • composer ui:icons:prune — remove unused icons only

Modified: Icon.php (Twig function)

meta_icon() now checks for local SVG files first, then falls back to iconify/json if installed. Twig usage is 100% unchanged — the SVG output is identical (verified).

composer.json

  • Package type changed to composer-plugin
  • iconify/json + iconify/json-tools moved from require to require-dev
  • composer-plugin-api + composer/composer added

Non-breaking

  • Existing projects: the Composer plugin auto-fetches icons on composer install/update, so meta_icon('mdi:home') keeps working identically
  • Backward compatible: if iconify/json is still installed, it works as fallback
  • SVG parity: verified that the API output matches iconify/json output exactly

Configuration (optional)

In the consuming project's composer.json:

{
    "extra": {
        "studiometa/ui": {
            "icons": {
                "enabled": true,
                "output": "assets/icons",
                "scan": ["templates", "app"],
                "include": ["mdi:loading"],
                "exclude": ["mdi:test-*"]
            }
        }
    }
}

Size impact

Approach Size
iconify/json (before) ~690MB
Build-time fetching (50 icons) ~25KB

Tests

  • 26 new tests (scanner, fetcher, storage, local fallback)
  • All 39 tests pass
  • phpcs + phpstan (max level) clean

titouanmathis and others added 4 commits February 13, 2026 20:10
Adds a Composer plugin that scans templates for meta_icon() calls and
fetches only the used icons from the Iconify API. Icons are stored as
local SVG files in a configurable output directory.

New classes:
- Plugin: hooks into post-install/post-update events
- Config: reads settings from composer.json extra section
- IconScanner: scans templates for meta_icon() references
- IconFetcher: batch-fetches icons from the Iconify API
- IconStorage: manages local SVG files
- IconSyncCommand: composer ui:icons [--dry-run] [--prune]
- IconPruneCommand: composer ui:icons:prune

Closes #498

Co-authored-by: Claude <claude@anthropic.com>
The meta_icon() Twig function now checks for local SVG files first
(stored by the Composer plugin), then falls back to iconify/json if
installed. This ensures full backward compatibility — existing projects
continue to work unchanged.

Co-authored-by: Claude <claude@anthropic.com>
The iconify/json and iconify/json-tools packages are no longer required
at runtime since the Composer plugin fetches icons at build-time. They
are kept in require-dev for testing the legacy fallback.

The package type is changed to composer-plugin so the Plugin class can
hook into post-install/post-update events and provide CLI commands.

Co-authored-by: Claude <claude@anthropic.com>
Tests for IconScanner, IconFetcher, IconStorage, and Icon local
file fallback. 26 new tests covering scanning, fetching, storage,
pruning, and the Twig function's local-file-first behavior.

Co-authored-by: Claude <claude@anthropic.com>
@github-actions
Copy link

Export Size

Unchanged

@studiometa/ui

Name Size Diff
AbstractFrameTrigger 1.74 kB -
AbstractPrefetch 366 B -
AbstractScrollAnimation 3.66 kB -
AbstractSliderChild 600 B -
Accordion 1.77 kB -
AccordionItem 1.75 kB -
Action 1.11 kB -
AnchorNav 3.85 kB -
AnchorNavLink 3.74 kB -
AnchorNavTarget 125 B -
AnchorScrollTo 2.53 kB -
animationScrollWithEase 763 B -
CircularMarquee 550 B -
Cursor 650 B -
DataBind 697 B -
DataComputed 856 B -
DataEffect 837 B -
DataModel 780 B -
Draggable 1.64 kB -
Fetch 2.34 kB -
Figure 1.72 kB -
FigureShopify 1.98 kB -
FigureTwicpics 2.26 kB -
FigureVideo 1.87 kB -
FigureVideoTwicpics 2.44 kB -
Frame 3.47 kB -
FrameAnchor 1.84 kB -
FrameForm 1.92 kB -
FrameLoader 1.45 kB -
FrameTarget 1.75 kB -
FrameTriggerLoader 1.46 kB -
Hoverable 953 B -
LargeText 713 B -
LazyInclude 322 B -
Menu 2.33 kB -
MenuBtn 140 B -
MenuList 1.9 kB -
Modal 1.99 kB -
ModalWithTransition 2.09 kB -
Panel 2.38 kB -
PrefetchWhenOver 408 B -
PrefetchWhenVisible 417 B -
ScrollAnimation 3.79 kB -
ScrollAnimationChild 3.91 kB -
ScrollAnimationChildWithEase 4.51 kB -
ScrollAnimationParent 3.98 kB -
ScrollAnimationTarget 3.85 kB -
ScrollAnimationTimeline 3.92 kB -
ScrollAnimationWithEase 4.39 kB -
ScrollReveal 1.63 kB -
Sentinel 129 B -
Slider 2.3 kB -
SliderBtn 817 B -
SliderCount 650 B -
SliderDots 1.86 kB -
SliderDrag 269 B -
SliderItem 998 B -
SliderProgress 961 B -
Sticky 771 B -
Tabs 1.38 kB -
Target 86 B -
Transition 1.41 kB -
withDeprecation 166 B -
withScrollAnimationDebug 2.04 kB -
withTransition 1.39 kB -

@codecov
Copy link

codecov bot commented Feb 13, 2026

Codecov Report

❌ Patch coverage is 94.45783% with 23 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.14%. Comparing base (52f37b3) to head (4be67a0).

Files with missing lines Patch % Lines
packages/twig-extension/Composer/IconStorage.php 87.67% 9 Missing ⚠️
packages/twig-extension/Composer/Plugin.php 89.65% 6 Missing ⚠️
packages/twig-extension/TwigFunctions/Icon.php 83.87% 5 Missing ⚠️
packages/twig-extension/Composer/IconFetcher.php 97.10% 2 Missing ⚠️
packages/twig-extension/Composer/IconScanner.php 96.96% 1 Missing ⚠️
Additional details and impacted files
@@              Coverage Diff               @@
##             develop     #499       +/-   ##
==============================================
+ Coverage      67.96%   87.14%   +19.18%     
- Complexity        20      134      +114     
==============================================
  Files             77       12       -65     
  Lines           1998      498     -1500     
  Branches         357        0      -357     
==============================================
- Hits            1358      434      -924     
+ Misses           558       64      -494     
+ Partials          82        0       -82     
Flag Coverage Δ
unittests 87.14% <94.45%> (+19.18%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/twig-extension/Composer/Config.php 100.00% <100.00%> (ø)
...kages/twig-extension/Composer/IconPruneCommand.php 100.00% <100.00%> (ø)
...ckages/twig-extension/Composer/IconSyncCommand.php 100.00% <100.00%> (ø)
.../twig-extension/Composer/PluginCommandProvider.php 100.00% <100.00%> (ø)
packages/twig-extension/Composer/IconScanner.php 96.96% <96.96%> (ø)
packages/twig-extension/Composer/IconFetcher.php 97.10% <97.10%> (ø)
packages/twig-extension/TwigFunctions/Icon.php 84.78% <83.87%> (-4.11%) ⬇️
packages/twig-extension/Composer/Plugin.php 89.65% <89.65%> (ø)
packages/twig-extension/Composer/IconStorage.php 87.67% <87.67%> (ø)

... and 73 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

titouanmathis and others added 7 commits February 13, 2026 20:16
Co-authored-by: Claude <claude@anthropic.com>
Add installation instructions for the Composer plugin (allow-plugins,
CLI commands, configuration options) and a tip in the Icon component
docs about automatic icon fetching.

Co-authored-by: Claude <claude@anthropic.com>
- Remove composer/composer from require-dev (causes dependency conflicts
  on PHP 8.1)
- Exclude Composer plugin classes from phpstan analysis (they depend on
  Composer classes available at runtime, not in vendor)
- Regenerate composer.lock with PHP 8.1 to ensure compatibility across
  the full CI matrix (8.1–8.4)

Co-authored-by: Claude <claude@anthropic.com>
- Bump PHP requirement from ^8.1 to ^8.3
- Upgrade pestphp/pest from ^2.0 to ^3.0
- Upgrade spatie/pest-plugin-snapshots from ^2.0 to ^2.3
- Add composer/composer ^2.9 as dev dependency for testing Composer plugin
- Update CI matrices to PHP 8.3, 8.4, 8.5

Co-authored-by: Claude <claude@anthropic.com>
- ConfigTest: 7 tests covering all config reading and path resolution
- PluginCommandProviderTest: 1 test verifying command registration
- IconSyncCommandTest: 8 tests covering sync, dry-run, prune, failures
- IconPruneCommandTest: 4 tests covering prune scenarios
- PluginTest: 6 tests covering event subscription and sync lifecycle
- Add helpers.php with createComposerStub and createCommandTester
- Update snapshots for Pest 3 compatibility

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
- Add mock Iconify API server (tests/Composer/fixtures/mock-api.php)
- All tests now use mock server instead of hitting the real Iconify API
- Add mockery/mockery dev dependency
- Remove phpstan excludePaths for Composer directory (passes at max level)
- Tests run in 0.4s instead of 1.8s

Co-authored-by: Claude <claude@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace iconify/json with build-time icon fetching (Composer plugin)

1 participant