getvoip_renderer
v1.5.7
Published
<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
Downloads
1
Readme
Renderer Server
- Philosophy
- Components Directory
- Component Structure
- API
- Static files
- Invoking a component's JS from its HTML and referencing the element
- JS component-to-component intercommunication
- child/nested components
Philosophy
- The renderer server generates code for compontents.
- Each component is an isolated code base that contains all of the js, css, html, and static files for that component.
- Each component is/can-be a separate git repo and is loaded as a submodule.
- 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. - 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). - 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 tobrowserify
). 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 havehtml.coffee
orhtml/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 objectapp
: 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 arehtml
,js
,css
, andgenerator
.generator
refers to the component's core generator files (i.e.html.js
,css.js
,js.js
). By default everything is purged iftypes
is not specifiedoptions
: 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:
- 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>
`
- 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
:
a string in which an empty
{}
object will be passed to child componentan 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" } ] }