Weave 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

Framework Laravel Package

assetic/framework

Assetic is a PHP asset management framework for combining, minifying, and compiling CSS, JavaScript, and other front-end assets through configurable filters and pipelines. It integrates with frameworks such as Symfony to streamline asset processing and delivery.

View on GitHub
Deep Wiki
Context7

Getting Started

Install

composer require assetic/framework

Note the package name/namespace mismatch: you install assetic/framework but everything lives under the Assetic\ namespace. It is the maintained fork of the original kriswallsmith/assetic, so older blog posts and the Symfony docs still apply almost verbatim.

Where to look first

  • src/Asset/ — FileAsset, StringAsset, AssetCollection, AssetCache. This is the core object model: an asset is anything that can be load()ed and dump()ed.
  • src/Filter/ — the built-in filters (CssMinFilter, JsMinFilter, UglifyJsFilter, LessFilter, SassFilter, CoffeeScriptFilter, CssRewriteFilter, …).
  • src/Factory/AssetFactory.php — the declarative front door: builds assets from string "formulas" and named filters.
  • src/AssetWriter.php and src/AssetManager.php — how you get compiled output onto disk.
  • tests/ — the most reliable documentation in the repo; each filter and factory option has a fixture-backed test.

First use case — concatenate and minify in a build step

use Assetic\Asset\AssetCollection;
use Assetic\Asset\FileAsset;
use Assetic\Filter\JsMinFilter;

$js = new AssetCollection([
    new FileAsset(__DIR__.'/assets/js/vendor.js'),
    new FileAsset(__DIR__.'/assets/js/app.js'),
]);
$js->ensureFilter(new JsMinFilter());
$js->setTargetPath('build/app.js');

file_put_contents(__DIR__.'/public/build/app.js', $js->dump());

Run that from a console command or a Composer script ("post-install-cmd": ["php bin/build-assets.php"]) and reference /build/app.js from your templates. That single pattern — collect, filter, dump to a web-served directory — is 80% of real-world Assetic usage.

Version floor

Pin at least ^3.2.3. That patch (PR #53) made @import validation consistent across the CSS-style and SCSS-style filters; before it, a given import path could be accepted by one filter and rejected by another depending on which one you had chained. If you compile both plain .css (with CssRewriteFilter/CssMinFilter) and .scss, start on 3.2.3 so you only debug one rule set.

Implementation Patterns

1. Explicit asset graph in a bootstrap/config file

For small apps, skip the factory and wire assets directly. It is the most debuggable form and has zero magic:

$css = (new AssetCollection([
    new FileAsset($root.'/assets/css/reset.css'),
    new FileAsset($root.'/assets/css/app.css'),
]))
    ->ensureFilter(new CssRewriteFilter())
    ->ensureFilter(new CssMinFilter());

$css->setTargetPath('build/app.css');

2. AssetFactory + named filters (the scalable pattern)

AssetFactory resolves paths relative to a root, expands globs, applies named filters, and attaches a target path — the natural home for an asset manifest:

use Assetic\Factory\AssetFactory;
use Assetic\Filter\CssMinFilter;
use Assetic\Filter\LessFilter;

$factory = new AssetFactory($root.'/assets', $debug = false);
$factory->setFilters([
    'less'   => new LessFilter('/usr/local/bin/lessc'),
    'cssmin' => new CssMinFilter(),
]);

$asset = $factory->createAsset(
    ['css/app.less', 'css/theme.less'],
    ['less', 'cssmin'],
    ['output' => 'build/app.css']
);

Keep this manifest in one file and reuse it from both your build command and (in dev) your request-time controller, so dev and prod produce identical output.

3. Deploy-time dump, not request-time compilation

Treat Assetic like a compiler, not a runtime library. Compile in CI or on composer install, commit/ship the public/build directory, and serve plain static files. Request-time filter execution is fine in development but will destroy latency and CPU under load — especially with filters that shell out (lessc, sass, uglifyjs).

If you must compile on demand, wrap with AssetCache:

use Assetic\Asset\AssetCache;
use Assetic\Cache\FilesystemCache;

$asset = new AssetCache($asset, new FilesystemCache($root.'/var/asset-cache'));

4. Cache busting with a ValueSupplier

Rather than hand-editing query strings, inject a value into asset names/content:

$asset->setValues(['version' => $appVersion]);
// then use %version% placeholders inside assets, or
$asset->setTargetPath(sprintf('build/app.%s.css', $appVersion));

A ValueSupplierInterface implementation backed by filemtime() of the source files is the idiomatic way to get per-file hashes.

5. Filter chains are ordered pipelines

ensureFilter()/FilterCollection apply in insertion order. Typical correct orders:

  • CSS: LessFilter → CssRewriteFilter → CssMinFilter
  • JS: CoffeeScriptFilter → UglifyJsFilter

Rewriting url() must happen after preprocessing (imports resolved) and before minification.

6. Framework integration

There is no Laravel/Symfony glue in this package. In Laravel, bind an AssetFactory as a singleton, register a Blade directive @asset('build/app.css') that appends the version hash, and put the dump logic in an artisan command (php artisan assets:build) invoked from your deploy script. For Symfony, use symfony/assetic-bundle if it is compatible with your framework version — otherwise prefer the two-line explicit pattern above; it avoids the bundle's container/config coupling entirely.

7. Serve from a single content-addressed directory

Write every asset to public/build/<name>.<hash>.css, never overwrite a live filename. This makes rollbacks trivial, keeps CDN/browser caches warm, and sidesteps the AssetCache invalidation problem described below.

8. Custom filters as extension points

If you need PostCSS, Tailwind, or image optimization, implement Assetic\Filter\FilterInterface (two methods: filterLoad() and filterDump()) and wrap your binary with Symfony\Component\Process\Process:

class PostCssFilter implements FilterInterface
{
    public function filterLoad(AssetInterface $asset) {}
    public function filterDump(AssetInterface $asset)
    {
        // run npx postcss, replace content via $asset->setContent()
    }
}

Other extension points worth knowing: Assetic\Factory\Worker\WorkerInterface (mutate assets as the factory creates them — this is how cache busting is implemented), Assetic\Factory\Loader\FormulaLoaderInterface (load asset definitions from YAML/JSON/DB), and AssetInterface itself for virtual assets.

9. Keep imports inside the source root

Since 3.2.3 the import validation rule is the same whether the @import is handled by the CSS-style filters or the SCSS filter. The practical consequence is that imports are validated against the asset's source root before the external binary ever sees them — so the safest, most portable manifest is one where every @import resolves under the configured root:

$factory = new AssetFactory($root.'/assets'); // everything under here
$factory->setFilters([
    'less' => new LessFilter('/usr/local/bin/lessc'),
]);

// assets/css/app.less may @import "theme.less" or "../shared/reset.less",
// but not "/home/ci/vendor/foo/bar.less"

If you genuinely need to pull in a vendor package's stylesheet (a copied node_modules directory, a Composer package's resources/), copy or symlink it into the source root as a build step rather than pointing the import at an absolute path outside it. That keeps behaviour identical between dev, CI, and prod, and it is the only shape that is guaranteed to keep working across patches.

Gotchas and Tips

External binaries are your #1 failure mode. Filters like LessFilter, SassFilter, UglifyJsFilter, CoffeeScriptFilter, and the Closure/YUI filters shell out to lessc, sass, uglifyjs, coffee, or java. They are not bundled. In CI, either install them explicitly or fail the build with a clear message. Pass absolute binary paths in the filter constructor instead of relying on PATH — cron/CI environments routinely have a different PATH than your shell.

Import validation changed in 3.2.3 (PR #53). Validation of @import paths was previously applied inconsistently between the CSS filters and the SCSS filter, so the same import could work through one chain and fail through another. It is now uniform. If you have imports that reach outside the asset's source root (absolute paths, ../../vendor/..., paths into node_modules), audit them after upgrading from 3.2.2 or earlier — an import that happened to slip through the CSS path may now be rejected, and the symptom is a missing ruleset rather than an exception if you are not watching build output. Treat ^3.2.3 as the floor for any project compiling both CSS and SCSS, and add one lessc/sass --version-style smoke test over your real manifest to catch it.

Filters apply at load() time, not at construction. Calling ensureFilter() after load() means the filter is silently ignored for that pass. Set all filters first, then dump(). If output looks unfiltered, this is almost always why.

AssetCache keys do not include filter configuration. Change your minifier options and you may still get the old cached dump. Clear the cache directory on every deploy, or include a config hash in the cache path (var/asset-cache/<config-hash>/). This is the single most common "why is my CSS stale" bug in Assetic.

CssRewriteFilter needs correct roots. url(...) rewriting depends on where the asset thinks it lives. Set $asset->setSourceRoot() / setTargetPath() accurately, or images and fonts will 404 from the compiled bundle. For anything served from a CDN or a different origin, test the output paths explicitly.

JS concatenation is not module-aware. Order matters, "use strict" in the first file applies to the whole bundle, and a missing trailing semicolon in file A breaks file B. JsMinFilter is a lightweight tokenizer — it can mangle edge-case ES6+ (template literals, regex-vs-division ambiguity, arrow functions). Either keep bundles ES5, or plug in UglifyJsFilter/TerserFilter-style tooling and verify output with a smoke test.

No source maps. Concatenation + minification produces a single blob with no mapping. In development, run the factory with $debug = true (filters bypassed, individual files served) and only trust optimized output in staging/prod. Make sure your templating helper switches the file list on the same flag.

Memory and timing. FileAsset::load() reads the entire file into memory, and AssetCollection loads all children. Bundling hundreds of megabytes of images through Assetic will exhaust memory. Restrict Assetic to text assets (CSS/JS) and leave images to a copy/optimize step.

Formula/asset-manager caching is subtle. If you use LazyAssetManager with formula loaders, formula files are typically only re-read in debug mode. In production the manager trusts its cache — so a typo in your manifest looks like "nothing happened." Verify by dumping with debug on at least once after any manifest change.

Namespace and package name do not match. composer require assetic/framework, but use Assetic\.... Autoloading is PSR-4 to src/. If you are migrating from kriswallsmith/assetic, remove the old package explicitly and check composer why — duplicate class definitions are easy to create.

Project health signal. Low star count, effectively zero dependents, and releases that arrive as long gaps punctuated by a single maintenance patch (v3.2.3 is one PR, a consistency fix, not a feature) point to a low-traffic fork kept alive by a small number of maintainers. That is not a reason to avoid it — a one-PR patch release that closes a real footgun is a healthy sign — but it does mean: pin an exact version ("assetic/framework": "3.2.3"), read the diff before bumping, and keep your integration surface small (a single AssetFactory bootstrap + one build command) so you can replace it with Webpack/Vite/PostCSS in an afternoon if maintenance stops. In 2026, the realistic verdict is unchanged: excellent for a dependency-free PHP build step with no Node toolchain; a liability if you need modern JS/CSS pipelines.

Debugging checklist when output is wrong: (1) call $asset->dump() in isolation before touching the writer, (2) temporarily remove all filters to confirm the raw concatenation is correct, (3) if a specific @import stops resolving after an upgrade, check it against the source root — 3.2.3 tightened this, (4) verify getTargetPath() and the writer's base directory resolve where you expect, (5) clear every cache directory, (6) confirm binary paths and versions with --version.

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.
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core