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-render

v2.2.0

Published

Lightweight JS library for transforming JSX into raw HTML, enabling both server-side and client-side rendering without a virtual DOM. It offers a simplified component model with automated event handling and global state management.

Downloads

460

Readme

als-render Documentation

1. Introduction

als-render is a frontend framework that offers a comprehensive solution for building dynamic components (components with dynamic state and hooks), importing them, integrating them with each other, with the ability to use a single global context, and with the ability to use a JSX-like syntax. The framework works on the fly both in the browser and on the server, offering SSR on the fly and allowing you to use the same files both on the server and in the browser. On the server, you can connect the browser part via routes in Express and do it on the fly. The framework also has the ability to localize for translation into pre-defined languages.

What's New in Version 2.1.0:

  1. Enhanced Rendering Flexibility:

    • Added support for custom parameters, scripts to execute before rendering, and scripts to execute after rendering.
    • Implementation includes a new render method and additional configuration options in the constructor.
    • Separate configurations for server-side rendering and bundling.
  2. Browser-Specific Enhancements:

    • Introduced support for custom parameters, pre-render scripts, and post-render scripts specifically for the browser version.
  3. Backward Compatibility:

    • The build method remains unchanged to ensure full backward compatibility.

2. Installation and Setup

Installation

You can install als-render via npm or use a CDN for quick testing in the browser:

  • npm (recommended for full development):
npm install als-render

Requirements

  • Browser: ES6 support is required.
  • Node.js: Compatible with all Node.js versions supporting ES6.

Quick Start

Components example

App.js:

const Counter = require('./Counter');

class App extends Component {
   constructor(props) { super(props)}
   render(props) {
      const { data } = props;
      return (
         <div>
            <Counter count={data.count} />
         </div>
      );
   }
}

module.exports = App;

Counter.js:

class Counter extends Component {
   constructor(props) { super(props)}
   render({ count }) {
      return (
         <div>
            <button onclick={() => this.update({ count: count + 1 })}>
               { 'Increase' }
            </button>
            <span>{ count }</span>
            <button onclick={() => this.update({ count: count - 1 })}>
               { 'Decrease' }
            </button>
         </div>
      );
   }
}

module.exports = Counter;

Browser Example

Here’s a minimal example of using als-render in the browser to create a counter component:

HTML:

<!DOCTYPE html>
<html lang="en">
<head>
   <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <script src="https://www.unpkg.com/[email protected]/render.min.js"></script>
   <title>Counter</title>
</head>
<body>
   <div component="App"></div>
</body>
<script>
   render('./App', { count: 0 });
</script>
</html>

Node.js Example (Server-Side Rendering)

You can use the same components for server-side rendering (SSR). Here’s an example:

Server Code:

const Render = require('als-render');
const fs = require('fs');

const rendered = new Render('./App');
const rawHtml = rendered.build({ count: 0 });
const html = `<!DOCTYPE html>
<html lang="en">
<head><title>Counter</title></head>
<body>${rawHtml}</body></html>
`
fs.writeFileSync('./counter.html', rawHtml, 'utf-8');

Example with Express

For dynamic SSR integration with Express:

const express = require('express');
const Render = require('als-render');
const app = express();

const rendered = new Render('./App');
Render.publish(app, express); // Adds bundle and public routes.

app.get('/', (req, res) => {
   const html = rendered.build({ count: 0 }, 'ru');
   res.send(`<!DOCTYPE html>
      <html lang="en">
      <head><title>Counter</title></head>
      <body>${html}</body>
   </html>`);
});

app.listen(3000, () => console.log('Server is running on http://localhost:3000'));

In the browser: The server dynamically generates the same HTML structure as the browser example and serves it, including all component functionalities.

Components

als-render uses als-component to create reactive components that work seamlessly in both server and browser environments. Components support lifecycle hooks, event handling, and state management.

For detailed information on component usage, refer to the als-component documentation.

By default the Component class available as global variable for rendered components.

It can be used like this:

class App extends Component {
   constructor(props) { super(props) }
   render({ count = 0 }) {
      return `
         <div>
            <button click="${this.action('click', () => this.update({ count: count - 1 }))}">-</button>
            <span>${count}</span>
            <button click="${this.action('click', () => this.update({ count: count + 1 }))}">+</button>
         </div>
      `;
   }
}

Or like this if options.jsx = true (true by default):

class App extends Component {
   constructor(props) { super(props) }
   render({ count = 0 }) {
      return (
         <div>
            <button onclick={() => this.update({ count: count - 1 })}>-</button>
            <span>{count}</span>
            <button onclick={() => this.update({ count: count + 1 })}>+</button>
         </div>
      );
   }
}

The Component has hooks mount and unmount and onload event:

this.on('mount', () => console.log('Component mounted'));
this.on('unmount', () => console.log('Component unmounted'));

3.2 Localization

als-render includes built-in localization support through a global _() function, available in every component. This function retrieves the appropriate translation for the current language.

Example:

Define your localization settings:

const langs = { 
   langs: ['en', 'ru'],
   dictionary: {
      Increase: ['Increase', 'Увеличить'],
      Decrease: ['Decrease', 'Уменьшить']
   }
}
const options = { langs,lang: 'ru'};

In your component:

class Counter extends Component {
   render({ count }) {
      return (
         <div>
            <button>{ _('Increase') }</button>
            <span>{ count }</span>
            <button>{ _('Decrease') }</button>
         </div>
      );
   }
}

In browser:

<script>
render('./App', {count:0}, { langs,lang: 'ru'});
</script>

On server:

const rendered = new Render('./App', {langs});
const lang = 'ru';
const rawHtml = rendered.build({ count: 0 }, lang);

Global variables

By default, there are few global variables which available for all components:

  1. context - is the object which passed from outside and available as global for all components
    1. publics - The object for adding static public files or directories right grom components
    2. routes - The object for adding routes with string content to add as app.get(url,(req,res) => res.send(content))
    3. isBrowser - true in browser and false in NodeJs
  2. _ - the localization function as showed above
  3. Custom variables added by developer

context

const rendered = new Render('./App');
const context = {some:'Value for node'}
const browserContext = {some:'Value for browser'}
const rawHtml = rendered.build({ count: 0 },context,browserContext);
class App extends Component {
   constructor(props) { super(props) }
   render({ count = 0 }) {
      if(context.isBrowser) {
         const existCount = localstorage.getItem('count')
         if(existCount == undefined) localstorage.setItem('count',count)
         else this.props.count = Number(existCount)
      }
      return (
         <div>
            {context.some}
            <button onclick={() => this.update({ count: this.props.count - 1 })}>-</button>
            <span>{this.props.count}</span>
            <button onclick={() => this.update({ count: this.props.count + 1 })}>+</button>
         </div>
      );
   }
}

context.publics and context.routes

The context.publics available both on browser and nodejs versions, but used only in nodejs.

context.publics['/app/styles.css'] = './styles.css'
context.routes['main-style.css'] = `body {margin:0}`
class App extends Component {
   ...
}
const rendered = new Render('./App');
Render.publish(app, express); // Adds bundle and public routes.

const rawHtml = rendered.build({});

Custom variables and scripts

New renderOptions and bundleRenderOptions in Constructor

Updated Constructor Interface

const renderInstance = new Render('./components/App', {
   langs: { langs: ['ru'], dictionary: { hello: ['привет'] } },
   includebundle: true,
   renderOptions: {
      parameters: [], // Additional parameters for server-side rendering
      scriptBefore: '', // Scripts executed before server-side rendering
      scriptAfter: '' // Scripts executed after server-side rendering
   },
   bundleRenderOptions: {
      parameters: [], // Additional parameters for the browser bundle
      scriptBefore: '', // Scripts executed before the browser bundle
      scriptAfter: '' // Scripts executed after the browser bundle
   }
});

Parameters

The parameters should be strings and can include default value, like ['some="test"',now=Date.now()].

Behavior of scriptBefore and scriptAfter

  • scriptBefore - script for execution before requiring files.
    • For example const SOME_TOKEN="token";. Now SOME_TOKEN available anywhere in rendered code.
  • scriptAfter - script for execution after requiring files.
    • For example you have access to modules and result (MainComponent class) variables.

!imprtant Server bundle function builded twice to get publics and routes. On first run custom parameters are not available and scriptBefore and scriptAfter can throw error

Method render instead build

render(data, lang, options = {}) {
   const { 
      context = {}, // Context for server-side rendering
      browserContext = {}, // Context for browser-side rendering
      parameters = [], // Parameters for server-side rendering
      bundleParameters = [] // Parameters for browser-side rendering (must be strings)
   } = options;
}
  • parameters - can be any js parameter from server. Even request and response variables.
  • bundleParameters - must be strings (e.g., through JSON.stringify) because they are passed to the browser bundle.

Example

const renderInstance = new Render('./components/App', {
   langs: { langs: ['ru'], dictionary: { hello: ['привет'] } },
   includebundle: true,
   renderOptions: {
      parameters: ['customParam'],
      scriptAfter: 'if(customParam) customParam.test = true;'
   },
   bundleRenderOptions: {
      parameters: ['customBundleParam'],
      scriptAfter: 'customBundleParam.test = true;'
   }
});

const customParam = { test: false };
const customBundleParam = { test: false };

const result = renderInstance.render(
   { key: 'value' }, 
   'ru', 
   {
      context: { user: 'test' },
      browserContext: { browser: true },
      parameters: [customParam],
      bundleParameters: [JSON.stringify(customBundleParam)]
   }
);

console.log(customParam.test); // true
console.log(result.includes('customBundleParam.test = true')); // true

SSR

There are few options for server side rendering.

  1. Don't include bundle for browser (set options.includebundle = false)
  2. Include bundle for browser (set options.includebundle = true, or leave it, cause it's true by default)
    1. Include bundle for browser as script inside page (default option)
      1. This option adds js code directly to html which encreasing the page size for download
    2. Include bundle as Express route
      1. This option, adds route to express routes (app.get...) and use bundle as script[src]
      2. Recomended for production, because bundle cached by browser
        1. You can add version to bundle in (options.version) for renewing the cache

Example:

const express = require('express');
const app = express();

const Render = require('als-render');
Render.env.bundleAsRoute = true
const rendered = new Render('./App',{ includebundle:true, version:'1.0.0' });
Render.publish(app, express); // Adds bundle and public routes.

Configuration Options

Render Class Interface

class Render {
   static Require: typeof Require;
   static publics: Record<string, string>; // Static public files or folders
   static routes: Record<string, string>; // Static routes for Express
   static env: {
      bundleAsRoute: boolean; // Add bundle as route (default: false)
      minify: boolean; // Minify the bundle (default: false)
      jsx: boolean; // Enable JSX (default: true)
      bundleName: string; // Name of the bundle function (default: 'bundleFn')
   };

   static publish(app: Express, express: typeof import('express')): void;

   constructor(path: string, options?: {
      includebundle?: boolean; // Include bundle in the result (default: true)
      langs?: { langs: string[], dictionary: Record<string, string[]> };
      defaultLang?: string; // Default language for `_()` (optional)
      cyclicDependencies?: boolean; // Allow cyclic dependencies (default: false)
      version:undefined; // Adds ?version={version} to bunlde as src
      renderOptions: {
         parameters?: string[], // Additional parameters for server-side rendering
         scriptBefore?: string='', // Scripts executed before server-side rendering
         scriptAfter?: string='' // Scripts executed after server-side rendering
      },
      bundleRenderOptions: {
         parameters?: string[], // Additional parameters for the browser bundle
         scriptBefore?: string='', // Scripts executed before the browser bundle
         scriptAfter?: string=''// Scripts executed after the browser bundle
      }
   });

   build(data: Record<string, any>, lang: string, context?: Record<string, any>, browserContext?: Record<string, any>): string;

   render(data: Record<string, any>, lang: string, options = {}) {
   const { 
         context = {}, // Context for server-side rendering
         browserContext = {}, // Context for browser-side rendering
         parameters = [], // Parameters for server-side rendering
         bundleParameters = [] // Parameters for browser-side rendering (must be strings)
      } = options;
   }
}

Browser render() Function Interface

async function render(
   path: string,
   data?: Record<string, any>,
   options?: {
      selector?: string; // CSS selector for root component
      version?: string; // Cache-busting version string (optional)
      jsx?: boolean; // Enable JSX syntax (default: true)
      context?: Record<string, any>; // Global context object (default: {})
      langs?: { langs: string[], dictionary: Record<string, string[]> }; // Localization settings
      lang?: string; // Current language for `_()`
      cyclicDependencies?: boolean; // Allow cyclic dependencies (default: false)
      logger?: Console; // Logger for debugging (default: console)
      parameters = {}; // must be {key:value}
      scriptBefore = '';
      scriptAfter = '';
   }
): Promise<void>;