Client JavaScript Packages
This guide explains how to organize, test, and deploy client-side JavaScript packages in a Lively application.
Package Boundaries
Small application entry points can live directly in public/. As client behavior grows, keeping reusable modules, their dependencies, and their tests in a workspace package gives that code an explicit boundary independent of the Ruby application.
Lively uses web-packages to maintain three distinct layers:
components/ # Authored JavaScript packages and their tests.
node_modules/ # Package-manager installation.
public/_components/ # Generated browser-facing projection.
public/application.js # Application entry point.
Only files in components/ and public/application.js are authored directly. Treat node_modules/ and public/_components/ as generated output.
Create a Package
For example, an application can place its presentation behavior in an internal package:
components/
presentation/
package.json
Presentation.js
test/
Presentation.js
Define the package entry point and test command in components/presentation/package.json:
{
"name": "@example/presentation",
"private": true,
"type": "module",
"exports": "./Presentation.js",
"scripts": {
"test": "node --test test/*.js"
}
}
The package can use any browser-ready JavaScript modules. web-packages projects those modules for static delivery; it does not require a bundling or transpilation step.
Configure the Workspace
Register internal packages as workspaces in the application's root package.json, then select the files and browser import names that should be deployed:
{
"private": true,
"workspaces": [
"components/*"
],
"scripts": {
"test": "npm test --workspaces --if-present"
},
"web-packages": {
"packages": {
"@example/presentation": {
"include": [
"Presentation.js"
],
"imports": {
"@example/presentation": "Presentation.js"
}
}
}
}
}
Install dependencies and generate the browser-facing projection:
$ bundle exec bake web:packages:install
$ bundle exec bake web:packages:update
The package is now available at /_components/@example/presentation/Presentation.js.
Add the Package to a Page
Lively pages expose JavaScript packages through an import map. A custom page can extend the default imports and load an application entry point:
class ApplicationPage < Lively::Page
IMPORTS = Lively::Pages::Index::IMPORTS.merge(
"@example/presentation" => "/_components/@example/presentation/Presentation.js"
).freeze
def initialize(body:)
super(
title: "Presentation",
body: body,
imports: IMPORTS,
modules: ["/application.js"]
)
end
end
The application entry point can then use the package by name:
import {Presentation} from '@example/presentation';
const presentation = new Presentation(document);
presentation.start();
Keep application startup and page-specific integration in public/application.js. Put reusable behavior in the package so it can be tested without starting the Ruby application.
Test and Verify Packages
Run the workspace tests directly with the package manager:
$ npm test
When generated packages are committed or deployed with the application, verify that they match the lock file and package configuration:
$ bundle exec bake web:packages:install frozen=true
$ bundle exec bake web:packages:check
The first command checks that dependency installation is reproducible. The second checks both the generated manifest and the contents of public/_components.
For details about package selection, import maps, and alternative package managers, see the Web Packages documentation.