npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

als-view

v0.1.5

Published

Server-side rendering framework for dynamic pages with on-the-fly component bundling, supporting SEO and layout management.

Downloads

210

Readme

als-view

⚠ Warning: Beta Testing Stage

This library is currently in beta testing and is not yet recommended for use in production environments. While it offers a powerful framework for dynamic server-side rendering, active development and testing are ongoing, and certain features or behaviors may be subject to change.

Introduction

als-view is a powerful yet straightforward Node.js library designed to simplify the server-side rendering of dynamic web pages with integrated, on-the-fly component rendering. Built to meet the needs of modern applications, als-view allows developers to create robust, SEO-friendly pages that render JSX-like components dynamically—no pre-build required.

By leveraging two core tools, als-layout and als-render, this library enables a fluid and high-performance approach to web page generation:

  1. SEO Optimization and Content Management: Using als-layout, als-view provides a DOM-like structure with rapid configuration of SEO elements, scripts, styles, and other dynamic content. This allows you to fully manage page content with flexible, server-side tools.

  2. Dynamic Component Rendering: The integration with als-render lets als-view render React-like components on the server and in the browser without any build step. JSX-like components work directly on the server and can be executed in the browser as well, bridging server and client seamlessly.

  3. Automatic Environment Handling: als-view supports both Development and Production modes, allowing efficient workflow adjustments. In Production mode, the library generates component bundles as separate files to optimize browser caching and load times.

Key Advantages

  • Simplified Dynamic Page Creation: als-view combines the ease of creating SEO-optimized dynamic pages with JSX-like component rendering.
  • Unified Rendering for Server and Client: Components are rendered on demand and can function in both server and browser environments without requiring a separate build process.
  • Production-Optimized Caching: In Production mode, als-view automatically writes component bundles to static files, allowing browsers to cache scripts efficiently for optimal performance.

Whether you’re building a full-fledged server-side rendered application or need a lightweight solution for server-rendered pages with reusable components, als-view offers the flexibility and performance to meet your needs.

Installation

Requirements

  • Node.js version 14 or higher

Install als-view

To get started with als-view, use npm to install the package:

npm install als-view

Initial Setup

To configure als-view for your project, initialize the environment settings. This setup allows you to specify paths, development modes, and logging options.

// Import als-view
const View = require('als-view');

// Set environment options (optional)
View.setEnv({
   publicPath: './public',         // Directory to store static files in Production mode
   dev: process.env.NODE_ENV !== 'production', // Enable Dev mode based on environment
   logger: console                  // Custom logger (optional)
});

Setting Up Your Directory Structure

For als-view to work efficiently, ensure your project’s directory structure includes a views folder with your JSX-like component files and a public folder where static files can be stored. For example:

my-project/
├── public/                  # Stores static files in Production mode
└── views/
    └── App.js               # JSX-like components

Now you’re ready to start creating pages and rendering components dynamically!

Initializing Project Structure with als-view

To quickly set up your project structure with als-view, use the provided copy.js script. This script creates a default folder structure within your project’s directory, which includes essential resources like layouts, views, pages, and error handling components.

Running the copy.js Script

  1. Run the Command: To execute the script, use the following command from your project's root directory. This will copy the default als-view folder structure into your project:

    node ./node_modules/als-view/build/copy
  2. Using the --overwrite Flag: The --overwrite flag allows you to overwrite any existing resources within the target directory. This is useful if you want to reset the folder structure or update it with the latest version:

    node ./node_modules/als-view/build/copy --overwrite

    Note: Use the --overwrite flag carefully, as it will replace any existing files in the target directory.

Folder Structure Created by the copy.js Script

After running the copy.js script, your project directory will contain the following resources structure:

my-project/
├── resources/
│   ├── index.js                  # Main entry point for configuring views and environment
│   ├── components/
│   ├── layouts/
│   │   └── main.js               # Base layout with core settings (e.g., SEO, scripts, styles)
│   ├── pages/
│   │   └── Root.js               # Example page component
│   └── views/
│       ├── main.js               # Main view setup using the base layout
│       └── error/
│           ├── error-codes.js    # Map of error codes to status messages
│           ├── error-page.js     # Error page view and layout configuration
│           └── styles.js         # CSS styles specific to error pages

Overview of the Created Files

  • resources/index.js:

    • Sets up the main configuration for als-view by setting environment variables such as publicPath, dirName, dev mode, and logger.
    • Imports all views to ensure they are accessible from the main View instance.
  • resources/layouts/main.js:

    • Defines a base Layout that includes core settings, such as viewport, title, and default styles or scripts.
    • This layout serves as the foundation for all views that will be rendered in the application.
  • resources/pages/Root.js:

    • A sample JSX-like component to serve as a starting point for creating new pages.
    • This component can be updated or duplicated to create additional pages as needed.
  • resources/views/main.js:

    • Configures the main view by cloning the base layout (layouts/main.js) and adding pages to the view.
    • Includes example code for adding a root page using the addPage method.
  • resources/views/error Folder:

    • Contains error-specific configurations:
      • error-codes.js: Map of HTTP status codes to user-friendly messages.
      • error-page.js: Sets up an error view with a custom layout for displaying error messages.
      • styles.js: Defines CSS styles for error pages.

Usage Example

Once the structure is created, you can use it directly in your application code to configure and serve views and pages. Here’s a quick example of setting up the root and error pages:

// Import the main View setup
const View = require('./resources');

// Serve the root page
app.get('/', (req, res) => {
   View.views.main.pages.root.response({}, req, res).end();
});

// Serve error pages
app.use((req, res) => {
   const status = 404;
   View.views.error.pages.error.response({ status }).end();
});

With this setup, your als-view project structure is ready to use for server-side rendering, including error handling and SEO configurations.

Core Concepts and Components

In als-view, the main point of interaction is the View class. Through this class, you can create and manage layouts, pages, and components, as well as generate responses dynamically based on user requests. Here’s a high-level overview of how the core concepts and components work together in als-view:

1. View: The Core Interface

View is the central class in als-view, managing layouts, pages, and rendering configurations. All actions within als-view—such as setting up base layouts, adding pages, configuring views, and creating responses—are performed through View.

  • Layout Management: View can create or receive a base layout, which defines core elements like styles, scripts, favicon, host, and SEO metadata.
  • Pages and Views: For each view (e.g., a section like a dashboard), you can define specific layouts and configuration. Pages within a view (e.g., users, products) inherit the layout and can add their own components or scripts as needed.

Workflow with View

  1. Create a Base Layout: The base layout is a Layout instance that defines common scripts, styles, and metadata to be shared across views.

    const Layout = require('als-layout')
    const baseLayout = new Layout('',{host:'http://some.com'})
       .viewport()
       .title('My Application');
       .favicon('/path/to/favicon.ico');
       // styles, links, scripts, description, ...
  2. Initialize a View with the Layout: Using the base layout, initialize a View for a specific section or purpose. For example, create a dashboard view to manage user, product, or analytics pages.

    const dashboardView = new View(baseLayout, 'dashboard');
  3. Define Pages within the View: Add pages to the View by specifying their names and optional configurations. Each page can include JSX-like components, adding a level of interactivity. For pages with components, RenderedPage can be used to dynamically render and serve components.

    dashboardView.addPage('users', { title: 'Users Page' });
    dashboardView.addPage('products', { title: 'Products Page' }, './views/Products');
  4. Use Hooks for Custom Logic: Hooks can be added at the View or Page level, allowing custom logic to execute before rendering. For example, a hook can modify data before it’s passed to the component for rendering.

    dashboardView.addHook((data, response) => {
       data.user = getUserInfo(); // Add user info to the data
    });
  5. Generate Responses: Once views and pages are defined, als-view allows you to access them directly by name for response generation. This provides an organized and reusable approach to creating dynamic responses for different parts of your application.

    const { users } = View.views.dashboard.pages
    users.response(data, req, res).end(200); // Send the response with generated HTML and scripts

Summary of Key Classes

  • Layout: The DOM-like structure that defines a page’s core elements. It serves as the basis for each View.
  • View: Manages layouts, pages, and hooks. Allows access to defined views and their pages for generating responses.
  • Page: Represents a standard server-rendered page, supporting SEO tags, custom hooks, and a structured response.
  • RenderedPage: Extends Page to include dynamically rendered JSX-like components, creating a seamless link between server and client.

Accessing and Using Views and Pages

With als-view, you can directly retrieve views and pages by name and create responses programmatically:

// Access a page directly from View
const usersPage = View.views.dashboard.pages.users;

// Create and send a response
const response = usersPage.response(data, req, res);
response.end(); // Sends the generated HTML and assets to the client

By focusing on View as the main interface, als-view enables a streamlined approach to building, organizing, and serving dynamic pages in server-side applications.

Development and Production Modes

als-view is designed to work flexibly in both Development (Dev) and Production (Prod) environments. By setting dev to true or false in Env, als-view automatically adjusts behavior to optimize performance and efficiency for the given environment.

Key Differences Between Dev and Prod Modes

1. Minification

In Production mode, als-view minimizes file sizes for improved load times. When dev is set to false:

  • HTML output from the layout is minified.
  • Component bundles are created as separate files and minified before they are sent to the client.

In Development mode, files are not minified, making it easier to read, debug, and troubleshoot the generated code.

2. Bundling Components

When creating a RenderedPage, als-view dynamically manages component bundling based on the environment:

  • Dev Mode (dev: true):

    • Scripts are added directly as inline bundles in the layout using the layout.script() method. This keeps all files in-memory and allows for faster adjustments without writing to disk.
    • This setup speeds up development by allowing real-time updates without needing to reload static files.
  • Prod Mode (dev: false):

    • The component scripts are written to the publicPath directory as separate, cacheable files using the Files class. These static files allow browsers to cache resources, which significantly reduces loading times for returning users.
    • Bundles are stored in the /bundles directory within publicPath, and each file is linked as a <script src> tag in the layout, allowing the browser to download them only when necessary.

Component-Specific Options in addPage

When adding a RenderedPage, als-view provides additional configuration options that influence the rendering of components and the behavior of script bundling.

  • includeBundle: Determines if the component’s JavaScript bundle should be included in the layout.

    • When set to true, the component bundle is created and added as a <script> tag.
    • When set to false, the page will render without the component bundle, which may be useful if you only want server-side rendering without any additional client-side interactivity.
  • selector: Specifies the element where the rendered component’s output will be injected within the layout.

    • The component will replace <content></content> within the selected element.
    • By default, selector is set to 'body', meaning the component’s HTML will be inserted into the body of the layout.

Example Usage of Dev/Prod Modes

Development Mode (In-Memory Bundling)

In Development mode, bundles are included as inline scripts. This example illustrates setting up a RenderedPage with a JSX-like component (App.js) that updates in real-time:

// Set Dev mode
View.setEnv({ dev: true, publicPath: './public' });

const view = new View(baseLayout, 'dashboard');
view.addPage('app', { title: 'App Page', includeBundle: true }, './views/App');

// Render and send response
const response = view.pages.app.response(data, req, res);
response.end();

In this case, the App.js component is rendered and its bundle is added inline in the HTML output, with no static files written to disk.

Production Mode (Static Bundling for Caching)

In Production mode, component bundles are created as separate, cacheable files. Below, the same example as above is shown in Production mode:

// Set Prod mode
View.setEnv({ dev: false, publicPath: './public' });

const view = new View(baseLayout, 'dashboard');
view.addPage('app', { title: 'App Page', includeBundle: true }, './views/App');

// Render and send response
const response = view.pages.app.response(data, req, res);
response.end();

Here, App.js is bundled as a standalone .js file in the public/bundles directory, allowing the client to cache it for subsequent page visits.

Using these modes, als-view adapts to the needs of both development and production environments, providing flexibility, speed, and optimized performance.

Using Hooks in als-view

als-view provides flexible hook management that allows you to modify data, layout elements, response status, and more before a page is rendered. Hooks are available at both the global View level and individual Page level, giving you control over both shared and specific behaviors across views and pages.

Global and Page-Level Hooks

  1. Global Hooks (View Level):

    • Hooks added at the View level apply to all pages within that view. These hooks are useful for shared functionality, such as injecting user data into all responses or setting up common layout modifications.
    const view = new View(layout, 'main');
    
    // Example global hook to add user data from the session to the response data
    view.addHook((data, response) => {
       data.user = response.req.session?.user || null;
    });
  2. Page-Level Hooks:

    • Hooks added at the Page level apply only to the specified page, allowing you to define page-specific behavior. For example, a hook on a profile page might check if a user is authenticated before rendering.
    const profilePage = view.addPage('profile', { title: 'User Profile' });
    
    // Example page-specific hook to ensure user is authenticated
    profilePage.addHook((data, response) => {
       if (!data.user) {
          response.status = 401; // Unauthorized
          response.layout.body.innerHTML = '<h1>Unauthorized Access</h1>';
       }
    });

Accessing and Modifying Hooks

Hooks are stored in an array (hooks) at both the View and Page levels. You can modify this array to reorder hooks, add new hooks, or remove existing ones. This flexibility allows you to fully control the sequence and behavior of your hooks.

// Reordering hooks for a view
view.hooks.reverse(); // Example: reversing the order of hooks

Parameters Available in Hooks

Each hook function receives three parameters:

  • data: An object containing the data that will be passed to the page for rendering. You can modify this data within the hook.
  • response: An instance of PageResponse, which contains properties like layout, status, req and res. etc.

Common Modifications in Hooks

  • response.layout: Modify the layout before it’s rendered. For example, add custom HTML to the body or update the title.
  • response.status: Set the HTTP status code for the response (e.g., 200, 404, 500).
  • data: Add or update properties within data, which is ultimately passed to the component during rendering. This is ideal for injecting session data or other context-specific information.

Hook Example: Modifying Layout, Status, and Data

Here’s a combined example showing how to use hooks at both the View and Page levels to handle layout modifications, custom status codes, and data enrichment.

// Global hook for all pages in the 'main' view
view.addHook((data, response) => {
   // Access session data and set a user property in data
   data.user = response.req.session?.user || null;
   
   // Modify the layout title based on the user session
   if (data.user) {
      response.layout.title(`Welcome, ${data.user.name}`);
   } else {
      response.layout.title('Welcome, Guest');
   }
});

// Page-specific hook to handle authentication for 'profile' page
const profilePage = view.addPage('profile', { title: 'User Profile' });
profilePage.addHook((data, response) => {
   if (!data.user) {
      // Redirect or display an error if not authenticated
      response.status = 403; // Forbidden
      response.layout.body.innerHTML = '<h1>Access Denied</h1>';
   }
});

In this example:

  • The view-level hook is used to set common data (user information from the session) and dynamically adjust the layout title.
  • The profilePage-level hook checks if the user is authenticated and modifies the response.status and response.layout.body accordingly.

Executing Hooks in Order

By default, hooks are executed in the order they are added. If you need to adjust the sequence, you can directly modify the hooks array:

// Adjust the order of hooks at the view level
view.hooks.reverse(); // Reverse the order of execution

Hooks in als-view provide powerful customization options, allowing you to set up complex data manipulations and response configurations that adapt to your application's needs.

API Reference for als-view

The als-view library provides a streamlined API for setting up server-side rendered views with layouts, pages, and component-based rendering. Below is a detailed explanation of each available class and their purpose.

Main Entry Point: View

The View class is the central interface for using als-view. Through the View class, you can:

  • Configure a base Layout for your application.
  • Add Page instances to create views for different routes.
  • Manage hooks at both the View and Page levels for custom functionality.

Static Properties and Classes

The View class exposes several static properties, which provide direct access to key classes used within the library:

  • View.Files: Exposes the Files class for managing file handling and script bundling.
  • View.Env: Exposes the Env class for environment configuration, such as setting paths, modes, and logging.
  • View.PageResponse: Exposes the PageResponse class, which represents the response for each page and provides methods for modifying the response and ending it.
  • View.Page: Exposes the Page class, representing a standard page without components.
  • View.RenderedPage: Exposes the RenderedPage class, which extends Page to support JSX-like components and client-side bundling.

Class Details

1. Files

The Files class handles file operations for scripts and static assets, including:

  • Constructor: Files(publicPath, dirName)
    • publicPath: The root path for public files.
    • dirName: The directory where bundled scripts are stored.
  • write(path, content, layout): Copies files from the path and writes the content to the specified location in the layout. This method automatically adds the .js extension if missing.

2. Env

The Env class manages the environment configuration and initializes essential settings for the application.

  • Constructor: Env(options = {})
    • options: Contains properties such as publicPath, dirName, dev mode, and logger.
  • init({ publicPath, dirName, dev, logger }): Initializes or updates environment options.
  • layout(layout): Returns a new Layout instance, minified if dev mode is disabled.

3. PageResponse

PageResponse is responsible for managing the response, allowing custom data, status, and HTML modifications.

  • Constructor: PageResponse(page, data, req, res)
    • page: The page instance associated with this response.
    • data: The data passed to the page.
    • req: The request object.
    • res: The response object.
  • end(status = this.status): Ends the response with the specified status, applying any modifications from the layout.

4. Page

The Page class represents a standalone page with its own layout, hooks, and response handling.

  • Constructor: Page(name, options, view)
    • name: A unique identifier for the page.
    • options: Page-specific options like url, title, description, and other SEO-related settings.
    • view: The parent view to which the page belongs.
  • addHook(hook): Adds a hook to the page’s hooks array.
  • response(data, req, res): Returns a PageResponse for handling HTTP responses.

5. RenderedPage

The RenderedPage class extends Page and supports client-side bundling and rendering for JSX-like components.

  • Constructor: RenderedPage(name, path, options = {}, view)
    • name: The name of the page.
    • path: The path to the component file.
    • options: Additional options like includeBundle and selector.
    • view: The parent view.
  • buildRender(path, options): Builds the component renderer and includes bundles based on includeBundle.
  • addBundle(): Manages bundle files, writing them to disk if in Prod mode or as inline scripts in Dev mode.

Example Usage

const View = require('als-view');
const layout = require('./resources/layouts/main'); // Main layout

// Initialize the environment with options
View.setEnv({
   publicPath: './public',
   dirName: 'bundles',
   dev: true,
   logger: console,
});

// Create a new view using the base layout
const mainView = new View(layout, 'main');

// Add a global hook to set user data for all pages in this view
mainView.addHook((data, response) => {
   data.user = response.req.session?.user || null;
});

// Add a `Page` with only server-side rendering
mainView.addPage('home', { title: 'Home Page' });

// Add a `RenderedPage` for component-based rendering with bundling
mainView.addPage('dashboard', { includeBundle: true, selector: 'body' }, './components/Dashboard');

// Access pages and render response
const response = mainView.pages.dashboard.response(data, req, res);
response.end();

The als-view library combines flexible server-side and client-side rendering capabilities, with hooks and layout handling that provide developers with powerful tools for creating dynamic and SEO-optimized web pages.