getvoip_renderer

1.5.7 • Public • Published

Renderer Server

Philosophy

  1. The renderer server generates code for compontents.
  2. Each component is an isolated code base that contains all of the js, css, html, and static files for that component.
  3. Each component is/can-be a separate git repo and is loaded as a submodule.
  4. A component contains three generators: js generator, css generator, html generator. These generators are loaded by the renderer server via require('<component>/<html|js|css>') and are expected to export a function which will be invoked with options every time the component is requested.
  5. Generators of a component are invoked independently of one another and are expecting to return a string which will be inserted into the DOM as is and are expected to be in a valid syntax respective to their type. For example, the string returned from the html generator should be valid html code. You may write, transpile, compile code in any way you wish as long as a valid syntax string is returned (e.g. you can write in SASS but you must compile it with node-sass within the css generator and return a valid css string).
  6. All returned strings from each generator will be placed on the DOM as is with the exception of the js generator in which the returned string will be passed through a bundler (simplyimport, an inteneded successor and a drop-in replacement to browserify). This means full module resolution and package management is supported in the js generator.

Components Directory

By default the server attempts to load components from ./components/<target>. This directory can be changed by setting the COMPONENTS_DIR env variable.

Component Structure

A component can be referenced by its directory name. For example, if you created a component under components/myComponent, its html can be reached via http://<render-server>/html/myComponent.

├─ myComponent
│  └─ package.json
│  └─ html.js
│  └─ css.js
│  └─ js.js
│  └─ static/

Notes

  • html, css, & js are referenced as "routes/generators" in this doc.
  • All routes are optional and are not required for a component to be operational. If a route doesn't exist for a given component and is requested by a user, an empty string will be returned.
  • All routes can be specified in any other 'requireable' file format. For example, instead of html.js you can have html.coffee or html/index.js.
  • If a route is included, the renderer server expects the route to export a function that will return either a string or a promise that eventually resolves a string.
  • When invoked, a route's function will receive 2 arguments by the renderer server:
    • options: custom/optional options specified by the user in the query string.
    • context: an object
      • app: the FeathersJS app of the renderer server.
      • dataclient: an instance of axios http client.
      • req: http.Request object (not always present).
      • res: http.Response object (not always present).
      • sharedState: a memory-persistent state object shared across all invoked components.
      • jsConnector: special function for communicating between a component's html and js generators
  • If included, the js route is expected to return valid javascript content.
  • If included, the css route is expected to return valid CSS content.
  • If included, the html route is expected to return valid HTML content.
  • A component can have a package.json file which will be read and have its listed dependencies installed before any route is invoked.
JS dependencies/bundles

Anything a component's js route returns will be passed through a module bundler which will resolve and include any referenced module/file/dependency relative to the component's root directory.

Example component

myComponent/html.js

module.exports = function(options, context) {
    return `
        <div id=${options.id}>
            Hello World!
        </div>
    `
}

API

All of the routes mentioned below accept an optional query string to specify customs that will be passed to components e.g. /html/userReview?id=12&name=David

GET /iframe/<component>[?<options>]

Returns an HTML document that can be used directly inside an iframe element (or as is) including the component's HTML, CSS, and JS.

GET /html/<component>[?<options>]

Returns the HTML content of a component or an empty string if the component doesn't support this route.

GET /css/<component>[?<options>]

Returns the CSS content of a component or an empty string if the component doesn't support this route.

GET /js/<component>[?<options>]

Returns the JS content of a component or an empty string if the component doesn't support this route.

Multi-component routes

Each route supports a POST endpoint which generates and combines the content of multiple components. Components and their options are specified via the request body in a <componentName>:<options> hash format.

POST /html
{
    "componentA1": {"abc":123},
    "componentB2": {},
    "componentC3": {"def":456},
}
POST /html

Returns the combined HTML content for multiple components.

POST /css

Returns the combined CSS content for multiple components.

POST /js

Returns the combined JS content for multiple components.

Static files

Static files for a given component can be stored under /<component-root>/static/ and can be referenced in the following syntax:

!static/<filename>.<ext>
GET /static/<component>/<filename>:

Returns the raw data from the component's /static/<filename> directory.

Example

Assuming the following directory strucutre:

├─ myComponent
│  └─ html.js
│  └─ static
│     └─ logo.png
│     └─ screenshot.jpg

html.js:

module.exports = ()=> {
    return `
    <div class="myComponent">
        <img class="myComponent-logo" src="!static/logo.png">
        <img class="myComponent-screenshot" src="!static/screenshot.jpg">
    </div>
    `
}

Caching and purging

GET /purge/<components>[?<types>][?<options>]

Purge specific components cache.

  • components: a comma separated string representing components to be purged. The * wildcard is allowed to purge all components at once.
  • types: a comma sepearted string representing which routes of a component should be purged. Available values are html, js, css, and generator. generator refers to the component's core generator files (i.e. html.js, css.js, js.js). By default everything is purged if types is not specified
  • options: represents the options object of the component to purge. To purge all generated results regardless of options pass * for this setting.

To sum up, in order to completely clear the cache of a component you'd do the following:

GET /purge/footer,header?options=*&types=html,css,js,generator
POST /cache/clear

Clears all cache including components and bundler caches.

Internal state listing

GET /_list/components

Returns a list of currently available components

GET /_list/cache

Returns a list of current cache keys

Invoking a component's JS from its HTML and referencing the element

Let's say you want to render the html of a component entirely with javascript (i.e. client-side rendering). You would need to tell javascript where in the page should the rendered element be place in. In order to do so, you can use the jsConnector function within the html generator like so:

context.jsConnector([optionsSignature, extra])

Arguments: - optionsSignature: the options object that the js generator receives (defaults to the options the html generator receives) - extra: optional additional argument to pass to the js generator Returns: - id: the unique id generated for you to put as the id of the target element you are referencing. - code: the <script> element the jsConnector rendered for you to put within the html (but outside of the element)

Consider that the components name is header:

html.js

module.exports = (options, {jsConnector})=> {
    {id, code} = jsConnector();
    return `
        <div id="${id}"></div>
        ${code}
    `
}

javascript.js

module.exports = (options, context)=> {
    return `
        export default function(element){
            $(element).append(...)
        }
    `
}

JS component-to-component intercommunication

Components can access each other during browser runtime by using the special component$ function or by using the component$ prefix in import paths.

componentA1

export name = 'abc'

componentB2 (with special function)

export number = 123
export together = component$('componentA1').name + number
console.log(together) //-> 'abc123'

componentB2 (with ES6 import)

import {name} from 'component$/componentA1'
export number = 123
export together = name + number
console.log(together) //-> 'abc123'

child/nested components

If you want to render a component within a component you must follow the following steps:

  1. include the html of the child within the html of the parent component wherever desired html.js
module.exports = async (options, {child})=>
    return `
        <div id="parent-component">
            ${await child('child-component', {optional:options})}
        </div>
    `
  1. declare the child component in the package.json of the parent component so the renderer server automatically includes the js and css of the child component everytime the parent component is rendered. package.json
{
    "name": "parent-component",
    "version": "1.0.0",
    "children": [
        "child-component-1",
        {
            "name": "child-component-2",
            "options": {"custom":"options"}
        },
        {
            "name": "child-component-3",
            "options": "*" // indicates to pass all options passed to parent
        }
    ]
}

There are 2 ways to declare children in package.json:

  1. a string in which an empty {} object will be passed to child component

  2. an object in the shape of {name, options} to indicate the name of the child component and the options object to pass to its generators. The value of options can be in two forms: a. an object which will be passed as is to the child component's generators b. a string representing the what data should be passed from the parent's options object. If to '*' then all of the options passed to the parent will be passed down to the child component generators. Otherwise, specify the property you'd like to inherit in stringified-object-path version like in the following example:

    var parentOptions = {
        label: 'Tabbable Content',
        tabs: [
            {name:'tab1', config:{...}},
            {name:'tab2', config:{...}},
            {name:'tab3', config:{...}}
        ]
    }
     
    // package.json
    {
        "name": "parent-component",
        "version": "1.0.0",
        "children": [
            {
                "name":"first-tab",
                "options":"tabs[0].config" // if we want to pass just the config object
            },
            {
                "name":"second-tab",
                "options":"tabs[1].config"
            },
            {
                "name":"third-tab",
                "options":"tabs[2].config"
            }
        ]
    }

Readme

Keywords

none

Package Sidebar

Install

npm i getvoip_renderer

Weekly Downloads

0

Version

1.5.7

License

ISC

Unpacked Size

146 kB

Total Files

119

Last publish

Collaborators

  • danielkalen