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

Stylus Laravel Package

nodejs-php-fallback/stylus

PHP wrapper that compiles Stylus stylesheets by falling back to the official Node.js compiler, with a simple API for turning .styl files into CSS. Useful for Laravel and other PHP projects that want the Stylus preprocessor without a native PHP port or extra build tooling.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the wrapper with Composer:

composer require nodejs-php-fallback/stylus

Node is optional. If stylus is resolvable on the machine, the wrapper shells out to it; otherwise it hands the source to a bundled pure-PHP renderer. That dual path is the entire point of the package — it lets a Laravel app ship compiled .styl assets on shared hosting or locked-down CI where you can't run npm install.

Where to look first, in order:

  1. vendor/nodejs-php-fallback/stylus/README.md — the usage example is the whole API surface, it really is that small.
  2. vendor/nodejs-php-fallback/stylus/src/Stylus.php — a thin class; confirm the exact render method name here rather than trusting blog posts (it has been compile() in recent tags, getCss() in older ones).
  3. vendor/nodejs-php-fallback/nodejs-php-fallback/src/ — the parent package does the Node detection, exec(), and the swap to the fallback. All the interesting behaviour and all the surprises live here.
  4. vendor/nodejs-php-fallback/stylus/package.json — note the pinned stylus version; it dates from the 2019 release.

Smallest useful first use case — compile once from a tinker session or a throwaway artisan command:

use NodejsPhpFallback\Stylus;

$stylus = new Stylus(base_path('resources/styl/app.styl'));

file_put_contents(
    public_path('css/app.css'),
    $stylus->compile()
);

If that produces CSS, the package works. Then verify which path ran (see Gotchas) before you build anything on top of it.

Implementation Patterns

Bind it as a singleton service. Don't new Stylus(...) in controllers. A small service provider plus a config/stylus.php holding source/output paths and an engine override (node, php, auto) gives you one place to change behaviour per environment:

$this->app->singleton(StylusCompiler::class, fn () => new StylusCompiler(
    config('stylus.source'),
    config('stylus.output'),
));

Compile on deploy, not on request. The natural Laravel slot is a php artisan stylus:compile command invoked from your deploy script (Envoyer, Forge deploy hook, CI). Write to public/css/app.css so asset() / mix() / Vite manifest versioning picks it up normally. This also keeps the fallback parser off the hot path — it is not fast.

Cache the result if you must compile at runtime. For dev-time or CMS-driven styling, wrap the render:

$css = Cache::remember(
    'stylus:'.md5_file($path),
    now()->addDay(),
    fn () => $compiler->render($path)
);

Keying on md5_file means edits invalidate automatically and you never compile per request.

Blade directive for local development. A @stylus directive that inlines the compiled CSS is genuinely nice in local, letting you skip the watcher. Register it only when app()->isLocal() — in production you want the static file.

Wrap the fallback in your own adapter. Subclass or decorate Stylus so that error handling, path resolution, and logging are yours, not the package's. This makes it trivial to swap in a direct proc_open('node ...') call later when you drop the dependency, which given the release date you should assume you eventually will.

Integration tip: point the wrapper at a .styl entry file that @imports the rest. The PHP fallback resolves imports relative to the file's directory imperfectly; a single shallow entry file with sibling imports is the most portable shape. Avoid @import from node_modules (e.g. @import 'nib') if you care about the fallback path working at all.

Gotchas and Tips

  • The fallback is silent. If Node fails — wrong binary, missing node_modules, a syntax error only the real Stylus catches — you may get PHP-rendered output that is almost right. Never assume success from a non-empty string. Log the engine actually used, and assert on a sentinel in the output (e.g. a /* compiled via node */ comment you add in the .styl file) during your own smoke tests.
  • Two engines means two dialects. The PHP renderer is a partial reimplementation. Expect gaps around @extend, @media nesting edge cases, arithmetic in property values, and functions/mixins with defaults. Write your .styl in the intersection of the two dialects, or force engine => 'node' in staging so drift shows up before prod.
  • node_modules location is the usual first failure. The wrapper looks for the Stylus binary in a specific path relative to the vendor package. Installing Node deps inside vendor/ is fragile (wiped by every composer install). Either keep them in a separate project directory and configure the path, or accept the PHP fallback. Also make sure a composer install --no-dev doesn't erase something your deploy assumed was there.
  • The 2019 pin. The Node stylus version referenced is old; new Stylus features (@use-style patterns, modern built-ins) won't be available, and Node ≥ 16/18 may trip it up. If you have Node, invoke a modern stylus via your own exec and treat this package purely as the fallback.
  • Shell escaping and Windows. The exec path builds a shell command. Filenames with spaces or apostrophes, and Windows path separators, are classic breakage. Keep source paths simple and forward-slashed (base_path() values are fine).
  • No source maps, no minification, no autoprefixer. This is a Stylus transpiler only. Pair with a separate post-processing step, or accept unminified output.
  • Security: never interpolate user input into .styl source. The Node exec path is a shell invocation, and Stylus can read files via imports.
  • Vendored and unmaintained. 12 stars, last release January 2019, zero dependents. Treat it as vendored code: pin the exact version, keep an adapter in your own namespace, and have an exit plan (Vite + stylus npm package is the modern answer for new Laravel apps — this package's remaining niche is "no Node anywhere in production").
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.
3mdikram/worldmoneywords
snowsoft/config
itmosoft/laravel-subscriptions
larva/laravel-ai-qwen
diephp/sequences
users-au/socialite-provider
twidpay/twid-logger
mrnewport/laravel-plaid
mrnewport/laravel-repo
mrnewport/laravel-docsign
mrnewport/laravel-stow
mrnewport/laravel-flow
mrnewport/laravel-priceable
mrnewport/laravel-nestedterms
fredmeng/laravel-apidoc-generator
itstructure/laravel-multilingual-tools
pdustdar/elasticsearch
markupai/php-sdk
rlacerda83/lumem-microservice-shipment
uni-method/symfony-lib