NPM(3) RSX Framework Manual NPM(3) NAME npm - Including npm packages in RSX bundles SYNOPSIS // Asset Bundle with npm imports class My_Bundle extends Rsx_Asset_Bundle_Abstract { public static function define(): array { return [ 'npm' => [ 'moment' => "import moment from 'moment'", 'axios' => "import axios from 'axios'", ], ]; } } // Usage in JavaScript const now = moment().format('YYYY-MM-DD'); const response = await axios.get('/api/data'); DESCRIPTION RSX bundles can include npm packages through Asset Bundles. The bundle system uses esbuild to compile npm imports into the vendor bundle, exposing them as global variables accessible throughout your application. Unlike traditional webpack/vite setups where you write import statements in your JavaScript files, RSX centralizes npm dependencies in Asset Bundle definitions. This provides: - Explicit dependency declaration in PHP - Automatic vendor/app bundle splitting - Tree-shaking of unused exports - No import statements needed in application code PACKAGE INSTALLATION All npm packages must be installed in the system directory: cd /var/www/html/system npm install The framework's esbuild runs from system/, so packages must exist in system/node_modules/. The project root package.json is for reference only - actual dependencies live in system/. ASSET BUNDLE SYNTAX Create an Asset Bundle extending Rsx_Asset_Bundle_Abstract with an 'npm' array in the define() method: class My_Library_Bundle extends Rsx_Asset_Bundle_Abstract { public static function define(): array { return [ 'npm' => [ 'global_name' => "import statement", ], ]; } } Each entry maps a global variable name to an ES module import statement. The global becomes available to all JavaScript in bundles that include this Asset Bundle. IMPORT FORMATS DEFAULT EXPORT Package exports a single default value: 'moment' => "import moment from 'moment'", 'axios' => "import axios from 'axios'", 'lodash' => "import _ from 'lodash'", Usage: moment(), axios.get(), _.map() NAMED EXPORT Package exports multiple named values. Each named export needs its own entry: // CORRECT - separate entries for each named export 'createApp' => "import { createApp } from 'vue'", 'ref' => "import { ref } from 'vue'", 'computed' => "import { computed } from 'vue'", Usage: createApp(), ref(), computed() IMPORTANT: You cannot combine multiple named exports into one global. This does NOT work: // WRONG - creates single value, not object with both 'vue' => "import { createApp, ref } from 'vue'", The global 'vue' would only contain createApp, not both functions. NAMESPACE IMPORT Import all exports as a namespace object: 'Shiki' => "import * as Shiki from 'shiki'", Usage: Shiki.createHighlighter(), Shiki.bundledLanguages SUB-PATH IMPORTS Many packages offer sub-path imports for tree-shaking: // Full package - includes everything 'shiki' => "import { createHighlighter } from 'shiki'", // Sub-path - only what you need 'createHighlighter' => "import { createHighlighterCore } from '@shikijs/core'", 'darkPlus' => "import darkPlus from '@shikijs/themes/dark-plus'", Sub-path imports significantly reduce bundle size. BUNDLE PLACEMENT Asset Bundles are auto-discovered when Module Bundles scan directories. Place your npm Asset Bundle alongside the components that use it: rsx/app/docs/components/ shiki_bundle.php # Asset Bundle with npm imports Docs_Code_Block.js # Component using shiki globals Docs_Code_Block.jqhtml The Module Bundle scanning rsx/app/docs/ will automatically include the Shiki_Bundle and its npm dependencies. VENDOR BUNDLE COMPILATION npm packages compile into the vendor bundle, separate from app code. This provides better caching since vendor code changes less frequently. REBUILD TRIGGERS The vendor bundle rebuilds when: - npm entries in Asset Bundles change - First HTTP request after cache clear - Running rsx:clean followed by page request IMPORTANT: Running rsx:bundle:compile alone may not rebuild the vendor bundle if the cache key hasn't changed. To force a complete rebuild: php artisan rsx:bundle:compile --clean Or trigger via HTTP request: curl http://localhost/your-page php artisan rsx:debug /your-page TROUBLESHOOTING NPM MODULE NOT FOUND ERROR Error: RSX Framework Error: NPM module "xyz" not found. Expected window._rsx_npm.xyz to be defined by the vendor bundle. Causes: 1. Package not installed in system/node_modules/ 2. Vendor bundle not rebuilt after adding npm entry 3. Import statement syntax error Solutions: 1. cd system && npm install 2. php artisan rsx:bundle:compile --clean 3. Verify import syntax matches package's actual exports VERIFYING PACKAGE EXPORTS Check what a package actually exports: # Default export grep -E "^export default" node_modules/pkg/dist/index.mjs # Named exports grep -E "^export \{|^export function|^export const" \ node_modules/pkg/dist/index.mjs BUNDLE SIZE OPTIMIZATION Use sub-path imports when available: // Before: 9.7 MB (all languages, themes) 'shiki' => "import { createHighlighter } from 'shiki'", // After: 2.4 MB (only what's needed) 'createHighlighter' => "import { createHighlighterCore } from '@shikijs/core'", 'langJs' => "import js from '@shikijs/langs/javascript'", 'themeDark' => "import dark from '@shikijs/themes/dark-plus'", EXAMPLES CHART.JS class Chart_Bundle extends Rsx_Asset_Bundle_Abstract { public static function define(): array { return [ 'npm' => [ 'Chart' => "import { Chart } from 'chart.js/auto'", ], ]; } } // Usage new Chart(ctx, { type: 'bar', data: {...} }); DATE-FNS (Tree-Shaking Friendly) class DateFns_Bundle extends Rsx_Asset_Bundle_Abstract { public static function define(): array { return [ 'npm' => [ 'format' => "import { format } from 'date-fns'", 'parseISO' => "import { parseISO } from 'date-fns'", 'addDays' => "import { addDays } from 'date-fns'", ], ]; } } // Usage format(parseISO('2024-01-15'), 'MMM d, yyyy'); VUE 3 class Vue_Bundle extends Rsx_Asset_Bundle_Abstract { public static function define(): array { return [ 'npm' => [ 'createApp' => "import { createApp } from 'vue'", 'ref' => "import { ref } from 'vue'", 'reactive' => "import { reactive } from 'vue'", 'computed' => "import { computed } from 'vue'", 'watch' => "import { watch } from 'vue'", ], ]; } } SEE ALSO bundle_api(3), jqhtml(3) RSX Framework 2025-12-09 NPM(3)