🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
580 lines
19 KiB
Plaintext
Executable File
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) |