Files
rspade_system/app/RSpade/man/bundle_api.txt
2025-12-09 21:16:16 +00:00

580 lines
19 KiB
Plaintext
Executable File

BUNDLE_API(3) RSX Framework Manual BUNDLE_API(3)
NAME
Bundle - RSX asset compilation and management system
SYNOPSIS
// Module Bundle (page entry point)
use App\RSpade\Core\Bundle\Rsx_Module_Bundle_Abstract;
class My_Bundle extends Rsx_Module_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
'bootstrap5', // Module alias
'Quill_Bundle', // Asset bundle (explicit)
'rsx/theme', // Directory (auto-discovers asset bundles)
__DIR__, // Module directory
],
];
}
}
// Asset Bundle (dependency declaration, auto-discovered)
use App\RSpade\Core\Bundle\Rsx_Asset_Bundle_Abstract;
class Tom_Select_Bundle extends Rsx_Asset_Bundle_Abstract
{
public static function define(): array
{
return ['npm' => ['tom-select']];
}
}
// Render in Blade
{!! My_Bundle::render() !!}
DESCRIPTION
RSX Bundles provide a radically simplified asset compilation system
compared to Laravel Mix or Vite. Instead of webpack configurations,
JSON manifests, and build scripts, you define a simple PHP class
with an array of what to include. The framework handles everything
else automatically.
Unlike Laravel's approach where you configure webpack, define entry
points, set up hot module replacement, and manage complex build
pipelines, RSX Bundles use a single 'include' array that accepts
any mix of directories, files, NPM packages, or other bundles.
The system automatically determines file types, resolves dependencies,
and compiles everything.
The Bundle system integrates directly with the Manifest, automatically
including JavaScript stubs for controllers and models. SCSS files are
compiled transparently. Vendor and application code are automatically
split for optimal caching.
Key differences from Laravel Mix/Vite:
- Laravel: Complex webpack.mix.js or vite.config.js files
- RSX: Simple PHP class with an include array
- Laravel: Manual configuration of entry points and outputs
- RSX: Automatic detection and compilation
- Laravel: Separate processes for JS bundling and CSS compilation
- RSX: Unified system handles all asset types
- Laravel: Manual versioning and cache busting setup
- RSX: Automatic hash-based cache busting
Benefits:
- No JavaScript build configuration needed
- Works immediately without npm run dev/build
- Automatic vendor/app code splitting
- Integrated with PHP class discovery
- Zero configuration SCSS compilation
CREATING A BUNDLE
CREATING A MODULE BUNDLE (page entry point):
1. Extend Rsx_Module_Bundle_Abstract
2. Implement define() method
3. Return configuration array with 'include' key
Example:
class Dashboard_Bundle extends Rsx_Module_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
'bootstrap5',
'rsx/app/dashboard',
],
'config' => [
'api_version' => '2.0',
],
];
}
}
CREATING AN ASSET BUNDLE (dependency declaration):
1. Extend Rsx_Asset_Bundle_Abstract
2. Place alongside components that need the dependency
3. Declare NPM modules, CDN assets, or direct file paths
Example:
class Chart_JS_Bundle extends Rsx_Asset_Bundle_Abstract
{
public static function define(): array
{
return [
'cdn_assets' => [
'js' => [
['url' => 'https://cdn.jsdelivr.net/npm/chart.js'],
],
],
];
}
}
BUNDLE TYPES
RSX has two types of bundles with distinct purposes:
MODULE BUNDLES (Rsx_Module_Bundle_Abstract)
Top-level bundles that get compiled and rendered on pages.
- Can scan directories via 'include' paths
- Can explicitly include Asset Bundles by class name
- Auto-discovers Asset Bundles in scanned directories
- Gets built via rsx:bundle:build
- Cannot include other Module Bundles
Example:
class Frontend_Bundle extends Rsx_Module_Bundle_Abstract {
public static function define(): array {
return [
'include' => [
__DIR__, // Directory scan
'rsx/theme', // Directory scan (auto-discovers asset bundles)
'Quill_Bundle', // Explicit asset bundle
],
];
}
}
ASSET BUNDLES (Rsx_Asset_Bundle_Abstract)
Dependency declaration bundles co-located with components.
- NO directory scanning - only direct file paths
- Declares CDN assets, NPM modules, watch directories
- Auto-discovered when Module Bundles scan directories
- Never built standalone - metadata consumed by Module Bundles
- Can include other Asset Bundles by class name
Example:
// /rsx/theme/components/inputs/select/Tom_Select_Bundle.php
class Tom_Select_Bundle extends Rsx_Asset_Bundle_Abstract {
public static function define(): array {
return [
'npm' => ['tom-select'],
];
}
}
AUTO-DISCOVERY
When a Module Bundle scans a directory, Asset Bundles found within
are automatically processed. Their CDN assets, NPM modules, and
config are merged into the parent bundle.
This allows components to declare their own dependencies without
requiring explicit inclusion in every Module Bundle that uses them.
Discovered Asset Bundles cannot have directory scan paths in their
'include' array. If an Asset Bundle needs directory scanning, it
must be explicitly included by class name in the parent Module Bundle.
BUNDLE PLACEMENT
MODULE BUNDLES exist at top-level module directories:
CORRECT:
/rsx/app/login/login_bundle.php - Module Bundle
/rsx/app/frontend/frontend_bundle.php - Module Bundle
ASSET BUNDLES live alongside their components:
CORRECT:
/rsx/theme/components/inputs/select/Tom_Select_Bundle.php
/rsx/theme/bootstrap5_src_bundle.php
MODULE BUNDLE COVERAGE:
Including __DIR__ in a Module Bundle automatically includes all
files in that directory and subdirectories recursively, while
auto-discovering any Asset Bundles found.
Example:
// /rsx/app/login/login_bundle.php
class Login_Bundle extends Rsx_Module_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
__DIR__, // Includes ALL of /rsx/app/login/ recursively
],
];
}
}
This single bundle covers:
/rsx/app/login/login_controller.php
/rsx/app/login/login_index.blade.php
/rsx/app/login/login_index.js
/rsx/app/login/accept_invite/...
/rsx/app/login/signup/...
... and all files in subdirectories
ADDING CUSTOM NPM MODULES
Application developers can include third-party npm packages in their bundles
by creating Asset Bundles co-located with their components:
// /rsx/theme/components/charts/Chart_JS_Bundle.php
class Chart_JS_Bundle extends Rsx_Asset_Bundle_Abstract
{
public static function define(): array
{
return [
'npm' => [
'Chart' => "import { Chart } from 'chart.js/auto'",
],
];
}
}
The 'npm' array maps global variable names to ES module import statements.
Each entry creates one global variable accessible in your JavaScript.
The package is included only when Module Bundles scan that directory.
This keeps bundle sizes smaller when components aren't used.
For detailed npm integration documentation including import formats,
troubleshooting, and examples, see npm(3).
INCLUDE TYPES
Module Aliases
Predefined in config/rsx.php:
'jquery', 'lodash', 'bootstrap5', 'vue', 'react'
Bundle Classes
Reference other bundles:
'Core_Bundle', 'Bootstrap5_Src_Bundle'
Bundle Aliases
Defined in config/rsx.php:
'bootstrap5_src' => Bootstrap5_Src_Bundle::class
Directories
Include all files recursively:
'rsx/app/dashboard'
Specific Files
Include individual files:
'rsx/lib/utils.js'
'rsx/theme/variables.scss'
NPM Modules
Use Asset Bundles with the 'npm' array. See npm(3) for details.
CDN Assets
External resources:
'cdn:https://unpkg.com/library.js'
Public Directory Assets
Static assets from public/ directories with automatic cache-busting:
'/public/sneat/css/core.css'
'/public/sneat/js/helpers.js'
These resolve to files in any public/ directory in rsx/. Resolution
cached in Redis for performance. Generates tags with filemtime() for
fresh cache-busting on each page render.
ROUTE EXTRACTION (include_routes)
Bundles can extract route definitions from directories without bundling
their assets. This enables Rsx.Route() calls for controllers outside
the bundle's include paths.
SYNTAX
class Frontend_Bundle extends Rsx_Module_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
__DIR__,
],
'include_routes' => [
'rsx/app/login', // Extract routes only, no assets
'rsx/app/admin',
],
];
}
}
USE CASE
The frontend module needs to generate URLs to the login module
(e.g., logout link) but shouldn't bundle login's JavaScript/CSS.
Using include_routes extracts route patterns and makes them
available to Rsx.Route() without including the module's assets.
HOW IT WORKS
- Scans directories for PHP files extending Rsx_Controller_Abstract
- Extracts #[Route] attribute patterns from public static methods
- Generates Rsx._define_routes() call in compiled bundle
- Routes become available via Rsx.Route('Controller', 'method')
EXAMPLE
// In frontend JavaScript (with login routes extracted)
const logout_url = Rsx.Route('Login_Controller', 'logout');
// Returns: '/logout'
PUBLIC ASSET INCLUDES
Bundles can include static assets from any public/ directory with
automatic cache-busting via filemtime().
SYNTAX
Prefix paths with /public/ in bundle includes:
'include' => [
'/public/sneat/css/core.css',
'/public/sneat/js/helpers.js',
]
RESOLUTION
Path "sneat/css/demo.css" resolves to first match across all public/
directories in manifest. Resolution cached in Redis indefinitely.
Searches:
rsx/public/sneat/css/demo.css
rsx/app/admin/public/sneat/css/demo.css
rsx/theme/public/sneat/css/demo.css
... (all public/ directories)
OUTPUT
CSS: <link rel="stylesheet" href="/sneat/css/demo.css?v={filemtime}">
JS: <script src="/sneat/js/helpers.js?v={filemtime}" defer></script>
The filemtime() call executes on each page render, providing fresh
cache-busting timestamps without rebuilding bundles.
ORDERING
Public assets output with CDN includes, before compiled bundle code.
Order preserved as listed in bundle definition:
1. CDN CSS assets
2. Public directory CSS
3. Compiled bundle CSS
4. CDN JS assets
5. Public directory JS
6. Compiled bundle JS
AMBIGUITY ERRORS
If multiple files match the same path, compilation fails:
RuntimeException: Ambiguous public asset request:
'sneat/css/demo.css' matches multiple files:
'rsx/public/sneat/css/demo.css',
'rsx/theme/public/sneat/css/demo.css'
Solution: Use more specific paths or rename files to avoid conflicts.
CACHING
- Path resolution cached in Redis indefinitely
- Cache validated on each use (file existence check)
- Stale cache automatically re-scanned
- filemtime() executes on each page render for cache-busting
RESTRICTIONS
- Only .js and .css files allowed
- Must start with /public/ prefix
- Files must exist in a public/ directory
- No PHP files allowed (security)
BUNDLE RENDERING
In Blade layouts/views:
{!! Dashboard_Bundle::render() !!}
Generates:
<link href="/bundles/Dashboard__vendor.abc123.css" rel="stylesheet">
<link href="/bundles/Dashboard__app.def456.css" rel="stylesheet">
<script src="/bundles/Dashboard__vendor.abc123.js"></script>
<script src="/bundles/Dashboard__app.def456.js"></script>
Never call from controllers - only from Blade files.
VENDOR/APP SPLIT
Files automatically split:
- vendor/: Files containing "vendor/" in path, NPM modules
- app/: Everything else
Benefits:
- Vendor files cached longer (rarely change)
- App files rebuilt on changes
- Smaller incremental builds
BUNDLE PROCESSORS
Transform files during compilation.
Configured globally in config/rsx.php.
Built-in processors:
- ScssProcessor: .scss → .css
- JqhtmlProcessor: .jqhtml → JavaScript
All processors receive ALL collected files,
decide what to process based on extension.
CREATING A PROCESSOR
class MyProcessor extends AbstractBundleProcessor
{
public static function get_name(): string
{
return 'myprocessor';
}
public static function get_extensions(): array
{
return ['myext']; // Extensions to process
}
public static function process(string $file, array $options = []): ?array
{
$content = file_get_contents($file);
// Transform content
$processed = transform($content);
return [
'content' => $processed,
'extension' => 'js', // Output extension
];
}
}
Register in config/rsx.php:
'bundle_processors' => [
App\RSpade\Processors\MyProcessor::class,
],
COMPILATION PROCESS
1. Resolve all includes to file list
2. Split into vendor/app buckets
3. Check cache (skip if unchanged)
4. Run processors on files
5. Add JavaScript stubs from manifest
6. Filter to JS/CSS only
7. Compile vendor and app separately
8. In production: concatenate into single files
JAVASCRIPT STUBS
Controllers with Ajax_Endpoint methods get stubs:
// Automatically included in bundles
class User_Controller {
static async get_profile(...args) {
return Ajax.call(Rsx.Route('User_Controller', 'get_profile'), args);
}
}
Note: Rsx.Route() generates type-safe URLs like /_ajax/User_Controller/get_profile
Models with fetch() methods get stubs:
class User_Model {
static async fetch(id) {
return Ajax.model_fetch('User_Model', id);
}
}
CONFIGURATION
Bundle config added to window.rsxapp:
'config' => [
'feature_flags' => ['new_ui'],
'api_version' => '2.0',
]
Access in JavaScript:
if (window.rsxapp.config.feature_flags.includes('new_ui')) {
// New UI code
}
CACHING
Development:
- Vendor files cached until dependencies change
- App files rebuilt on any change
- Cache keys based on file hashes
Production:
- All files concatenated and minified
- Cache forever with hash in filename
- Rebuild only via rsx:bundle:compile
REQUIRED BUNDLES
Automatically included if used:
- jquery (if $ or jQuery detected)
- lodash (if _ detected)
- jqhtml (if .jqhtml files present)
FILE ORGANIZATION
storage/rsx-build/bundles/
├── Dashboard__vendor.abc123.js
├── Dashboard__vendor.abc123.css
├── Dashboard__app.def456.js
└── Dashboard__app.def456.css
Hash changes when content changes.
EXAMPLES
// Kitchen sink bundle
class App_Bundle extends Rsx_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
// Required modules
'jquery',
'lodash',
'bootstrap5',
// Other bundles
'Core_Bundle',
// Application code
'rsx/app',
'rsx/lib',
// Specific overrides
'rsx/theme/variables.scss',
// NPM packages
'npm:axios',
'npm:chart.js',
],
'config' => [
'app_name' => 'MyApp',
'version' => '1.0.0',
],
];
}
}
// Module-specific bundle
class Admin_Bundle extends Rsx_Bundle_Abstract
{
public static function define(): array
{
return [
'include' => [
__DIR__, // Include bundle's directory
'rsx/lib/admin',
],
];
}
}
CIRCULAR DEPENDENCIES
Framework detects and prevents circular includes:
- A includes B, B includes A = error
- Shows clear error with dependency chain
TROUBLESHOOTING
Bundle not updating:
php artisan rsx:bundle:compile My_Bundle --force
Missing files:
Check paths are relative to project root.
Verify files exist in manifest.
Processor not running:
Check processor registered in config.
Verify file extension matches.
SEE ALSO
npm(3), manifest_api(3), jqhtml(3), controller(3)
RSX Framework 2025-12-09 BUNDLE_API(3)