Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Asset Mapper Laravel Package

symfony/asset-mapper

Symfony AssetMapper exposes asset directories and publishes them to a public folder with digested (versioned) filenames. It can also generate an importmap, letting you use modern JavaScript modules without a build step.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to First Use

  1. Install the Package

    composer require symfony/asset-mapper
    

    Note: Laravel does not natively support this package, so manual integration is required.

  2. Configure Asset Mapping Create a Symfony-style configuration in config/packages/asset_mapper.yaml (or adapt to Laravel’s config/app.php):

    framework:
        asset_mapper:
            public_directory: '%kernel.project_dir%/public/build'
            mappings:
                js: ['%kernel.project_dir%/resources/js']
                css: ['%kernel.project_dir%/resources/css']
    

    For Laravel, use a service provider to register the mapper.

  3. Run the Mapper Use Symfony’s CLI (or create a Laravel Artisan command):

    php bin/console asset:map
    

    This copies assets to public/build with hashed filenames (e.g., app.[hash].js).

  4. Generate an Importmap

    php bin/console importmap:dump
    

    Outputs importmap.json to define ES module paths (e.g., "app.js": "/build/app.[hash].js").

  5. Use in Blade/Templates Reference assets via hashed paths:

    <script type="importmap">
        {{ file_get_contents(public_path('build/importmap.json')) }}
    </script>
    <script src="{{ asset('build/app.[hash].js') }}" type="module"></script>
    

First Use Case: Static Asset Versioning

Replace manual filename hashing (e.g., styles.css?v=123) with auto-generated digests:

// Before: Manual hashing
<link href="/css/styles.css?v={{ filemtime(public_path('css/styles.css')) }}" rel="stylesheet">
// After: AssetMapper
<link href="{{ asset('build/styles.[hash].css') }}" rel="stylesheet">

Run php artisan asset:map to update hashes on deploy.


First Use Case: ES Modules Without a Bundler

Enable import statements in vanilla JS:

// resources/js/app.js
import { createApp } from 'https://esm.sh/vue';
<!-- Blade template -->
<script type="importmap">
    {{ file_get_contents(public_path('build/importmap.json')) }}
</script>
<script type="module" src="{{ asset('build/app.[hash].js') }}"></script>

Run php artisan importmap:dump to auto-generate the importmap.


Implementation Patterns

Workflow: Laravel Integration

  1. Service Provider Setup Create app/Providers/AssetMapperServiceProvider.php:

    use Symfony\Component\AssetMapper\AssetMapper;
    use Symfony\Component\AssetMapper\AssetMapperBundle\DependencyInjection\Configuration;
    
    class AssetMapperServiceProvider extends ServiceProvider
    {
        public function register()
        {
            $this->app->singleton(AssetMapper::class, function ($app) {
                $config = new Configuration();
                $mappedConfig = $config->getMappedConfiguration(
                    $app['config']['asset_mapper'] ?? []
                );
                return new AssetMapper($mappedConfig);
            });
        }
    }
    
  2. Artisan Command Wrapper Extend Symfony’s commands in app/Console/Commands/MapAssets.php:

    use Symfony\Component\AssetMapper\Command\MapAssetsCommand;
    
    class MapAssets extends Command
    {
        protected $signature = 'asset:map';
        protected $description = 'Map assets to public directory with hashed filenames';
    
        public function handle()
        {
            $command = new MapAssetsCommand();
            return $command->run(new Application(), ['command' => $this->getName()]);
        }
    }
    
  3. Configuration in config/app.php

    'asset_mapper' => [
        'public_directory' => public_path('build'),
        'mappings' => [
            'js' => [resource_path('js')],
            'css' => [resource_path('css')],
        ],
        'importmap' => [
            'output_path' => public_path('build/importmap.json'),
        ],
    ],
    

Pattern: Incremental Adoption

  1. Start with Static Assets Use asset:map for CSS/JS/images without importmap:

    php artisan asset:map
    

    Benefit: Cache-busted filenames with zero config changes.

  2. Add Importmap Later Enable ES modules for new features:

    php artisan importmap:dump
    

    Benefit: Modern JS without a bundler.

  3. Hybrid Approach Combine with Laravel Mix/Vite:

    • Use asset:map for third-party libraries (e.g., node_modules).
    • Use Mix/Vite for app-specific bundles.

Pattern: Dynamic Asset Mapping

Extend the mapper for runtime-generated assets (e.g., user uploads):

use Symfony\Component\AssetMapper\AssetMapperInterface;

class CustomAssetMapper implements AssetMapperInterface
{
    public function map(string $path): string
    {
        if (str_starts_with($path, 'uploads/')) {
            return 'build/uploads/' . pathinfo($path, PATHINFO_FILENAME) . '.' . hash('sha256', filemtime($path));
        }
        return $path; // Fallback to default mapper
    }
}

Register as a binding in the service provider.


Pattern: Importmap for CDN Assets

Define external modules (e.g., React from a CDN) in importmap.json:

{
    "imports": {
        "react": "https://esm.sh/react@18",
        "react-dom": "https://esm.sh/react-dom@18"
    }
}

Generated via php artisan importmap:dump --cdn.


Gotchas and Tips

Pitfalls

  1. Laravel-Symfony Architecture Mismatch

    • Issue: AssetMapper expects Symfony’s HttpKernel and FrameworkBundle.
    • Fix: Mock dependencies or use a facade layer:
      $kernel = $this->app->make(KernelInterface::class);
      $mapper = new AssetMapper($config, $kernel->getContainer());
      
  2. Caching Conflicts

    • Issue: Laravel’s cache (e.g., config_cache.php) may override AssetMapper configurations.
    • Fix: Clear caches after changing asset_mapper settings:
      php artisan config:clear
      php artisan cache:clear
      
  3. Importmap JSON Injection

    • Issue: importmap.json may expose internal paths if not sanitized.
    • Fix: Use AssetMapper\ImportMap\ImportMapGenerator with a whitelist:
      $generator = new ImportMapGenerator();
      $generator->addAllowedPath(resource_path('js'));
      
  4. Concurrent Request Flooding

    • Issue: High traffic may trigger rate limits (e.g., CDN calls in importmap:dump).
    • Fix: Use --batch flag:
      php artisan importmap:dump --batch=10
      
  5. Blade Asset Helper Conflicts

    • Issue: Laravel’s asset() helper may not resolve hashed paths.
    • Fix: Create a custom helper:
      if (!function_exists('asset_mapper')) {
          function asset_mapper($path) {
              return asset('build/' . pathinfo($path, PATHINFO_FILENAME) . '.' . hash('sha256', filemtime(public_path($path))));
          }
      }
      

Debugging Tips

  1. Dry Run Mode Test changes without writing files:

    php artisan asset:map --dry-run
    php artisan importmap:dump --dry-run
    
  2. Verbose Output Enable debug logs:

    php artisan asset:map -v
    

    Look for AssetMapper entries in storage/logs/laravel.log.

  3. Manual Path Resolution Check mapped paths:

    $mapper = app(AssetMapper::class);
    dd($mapper->map(resource_path('js/app.js')));
    
  4. Importmap Validation Validate JSON syntax:

    php artisan importmap:dump && php -r "echo json_last_error();"
    

Extension Points

  1. Custom Digesters Override filename hashing:

    use Symfony\Component\AssetMapper\Digester\DigesterInterface;
    
    class CustomDigester implements DigesterInterface
    {
        public function digest(string $content): string
        {
            return hash('crc32b', $content); // Faster but less unique
        }
    }
    

    Register in the service provider.

  2. Asset Filters Exclude files (e.g., .min.js):

    use Symfony\Component\AssetMapper\AssetMapperInterface;
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport