Sign Up for Free

RunKit +

Try any Node.js package right in your browser

This is a playground to test code. It runs a full Node.js environment and already has all of npm’s 400,000 packages pre-installed, including red-agate with all npm packages installed. Try it out:

require("puppeteer-core/package.json"); // puppeteer-core is a peer dependency. require("puppeteer/package.json"); // puppeteer is a peer dependency. var redAgate = require("red-agate")

This service is provided by RunKit and is not affiliated with npm, Inc or the package authors.

red-agate v0.2.19

Static HTML|XML|SVG renderer using JSX, suitable for report output.

RedAgate

Static HTML | XML | SVG renderer using JSX, suitable for report output.

RedAgate is static HTML | XML | SVG renderer.
You can start easily because we are using JSX and semantics similar to React.

npm GitHub release Travis GitHub forks GitHub stars

Advantages:

  • Easily to bundle resources (images, stylesheets, fonts, scripts, ...) .
    RedAgate.renderAsHtml() API and component lifecycle defer() method return promise objects.
    You can use standard Tag-Libs (e.g. Image, Style, Font, SingleFont, Script, Asset) to bundle them.

  • Many standard Tag-Libs (e.g. If, Repeat, ForEach, Template, Html5, Svg, SVG shapes, Barcodes (QR Code, Code39, Code128, EAN/UPC, ITF, NW7/Codabar, postal barcode) and complex objects) are bundled.

  • Html5 Canvas API is available in the sub tree of the Svg component.

  • Running on both server side (Node.js) and modern browsers (Chrome, Firefox, Safari, Edge).

RedAgate

Install

$ npm install red-agate --save

Note

To import this from your code, you need to use babel + webpack and import red-agate-*/modules/* paths.
(We have used the import statements for doing the tree-shaking. The import statements in the .js not the .mjs files cannot import from the vanilla node.js.)

You can also import from the .mjs file on a node with the --experimental-modules option enabled.

Usage

See live demo on browser (code) and Node.js example.

Hello, world:

/** @jsx RedAgate.createElement */
import * as RedAgate from 'red-agate/modules/red-agate';

interface HelloProps extends RedAgate.ComponentProps {
    name: string;
}

const Hello = (props: HelloProps) => {
    return (<div>Hello, {props.name}!</div>);
};

RedAgate.renderAsHtml(<Hello name={'😈RedAgate😈'}/>)
.then(html => console.log(html))
.catch(error => console.log(error))

Defining element by using lambda:

export interface IfProps extends RedAgate.ComponentProps {
    condition: boolean;
}

export const If = (props: IfProps) => {
    if (this.props.condition) return this.props.children;
    else return [];
};

Defining element by using component:

export interface IfProps extends RedAgate.ComponentProps {
    condition: boolean;
}

export class If extends RedAgate.RedAgateComponent<IfProps> {
    public constructor(props: IfProps) {
        super(props);
    }

    // Equivalent to React's render() .
    public transform() {
        if (this.props.condition) return this.props.children;
        else return [];
    }
}

Defining SVG element by using component:

import { SvgCanvas }          from 'red-agate-svg-canvas/modules/drawing/canvas/SvgCanvas';
import { Shape,
         CONTEXT_SVG_CANVAS } from 'red-agate/modules/red-agate/tags/Shape';

export interface RectProps extends ShapeProps {
    width: number;
    height: number;
}

export const rectPropsDefault: RectProps = Object.assign({}, shapePropsDefault, {
    width: 10,
    height: 10
});

export class Rect extends Shape<RectProps> {
    public constructor(props: RectProps) {
        super(Object.assign({}, rectPropsDefault, props));
    }

    public render(contexts: Map<string, any>, children: string) {
        const canvas: SvgCanvas = this.getContext(contexts, CONTEXT_SVG_CANVAS);
        canvas.rect(0, 0, this.props.width, this.props.height);
        return ``;
    }
}

Complete example:

/** @jsx RedAgate.createElement */
import * as RedAgate     from 'red-agate/modules/red-agate';
import { ForEach,
         If,
         Template }      from 'red-agate/modules/red-agate/taglib';
import { Html5 }         from 'red-agate/modules/red-agate/html';
import { Svg,
         Group,
         Rect,
         Text,
         GridLine,
         SvgImposition } from 'red-agate/modules/red-agate/svg';
import { Font,
         Image,
         Style }         from 'red-agate/modules/red-agate/bundler';
import { query }         from 'red-agate/modules/red-agate/data';
import { Lambda }        from 'red-agate/modules/red-agate/app';
import { HtmlRenderer }  from 'red-agate/modules/red-agate/renderer';

interface FbaDetail {
    id: string;
    name: string;
    condition: string;
}
interface PrintJob {
    details: FbaDetail[];
}

const designerMode = true;
const font = "'Noto Sans', sans-serif";
const Fba = (props: {leaf: FbaDetail}) =>
    <Template>
        <Group x={0} y={0}>
            <Text x={27} y={11.5}
                textAlign="center" font={`11.5px 'Libre Barcode 128 Text', cursive`} fill
                text={leaf.id} />
            <Text x={4} y={18 + 3.5}
                font={`3.5px ${font}`} fill
                text={leaf.name} />
            <Text x={4} y={22 + 3.5}
                font={`3.5px ${font}`} fill
                text={leaf.condition} />
        </Group>
    </Template>;

export const fbaA4ReportHandler: Lambda = (event: PrintJob, context, callback) => RedAgate.renderOnAwsLambda(
<Html5>
    <head>
        <title>FBA</title>
        <link href="https://fonts.googleapis.com/css?family=Noto+Sans" rel="stylesheet"/>
        <link href="https://fonts.googleapis.com/css?family=Libre+Barcode+128+Text" rel="stylesheet"/>
        <Style src="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.3/normalize.css"/>
        <Style src="https://cdnjs.cloudflare.com/ajax/libs/paper-css/0.3.0/paper.css"/>
        <style dangerouslySetInnerHTML={{ __html: require('./fba-a4.style.css') }}/>
    </head>

    <body class="A4">
        <ForEach items={query(event.details).groupEvery(40).select()}> { (items: FbaDetail[]) =>
            <section class="sheet" style="position: relative; top: 0mm; left: 0mm;">
                <Svg width={210 - 1} height={297 - 2} unit='mm'>
                    <SvgImposition items={items} paperWidth={210} paperHeight={297} cols={4} rows={10}> { (item: FbaDetail) =>
                        <Template>
                            <If condition={designerMode}>
                                <Rect x={0} y={0} width={210 / 4} height={297 / 10} lineWidth={0.5} stroke/>
                                <GridLine startX={0} startY={0} endX={210 / 4} endY={297 / 10} gridSize={5} bleed={0} lineWidth={0.1}/>
                            </If>

                            <Fba leaf={item} />
                        </Template> }
                    </SvgImposition>
                </Svg>
            </section> }
        </ForEach>
    </body>
</Html5>, callback);
const event = {
    details: [{
        // ...
    }]
};

fbaA4ReportHandler(event /* PrintJob */, {} as any /* Context */, (error, result) => {
    if (error) {
        console.log(error);
    } else {
        console.log(result);
    }
});

Render html into PDF:

/** @jsx RedAgate.createElement */
import * as RedAgate    from 'red-agate/modules/red-agate';
import { Html5 }        from 'red-agate/modules/red-agate/html';
import { Lambda }       from 'red-agate/modules/red-agate/app';
import { HtmlRenderer } from 'red-agate/modules/red-agate/renderer';

interface PrintJob { /*  */ }

export const reportHandler: Lambda = (event: PrintJob, context, callback) => RedAgate.renderOnAwsLambda(
<Html5>
    hello, { event.name }!
</Html5>, callback);

export const pdfHandler = HtmlRenderer.toPdfHandler(reportHandler, {}, {
    width: '210mm',
    height: '297mm',
    printBackground: true,
});

pdfHandler(event /* PrintJob */, {} as any /* Context */, (error, result) => {
    if (error) {
        console.log(error);
    } else {
        console.log(result);
    }
});

Call from another process:

/** @jsx RedAgate.createElement */
import * as RedAgate     from 'red-agate/modules/red-agate';
import { Html5 }         from 'red-agate/modules/red-agate/html';
import { App }           from 'red-agate/modules/red-agate/app';

export const billngReportHandler = (event: BillingPrintJob, context, callback) => RedAgate.renderOnAwsLambda(
<Html5>billng</Html5>, callback);

export const kanbanReportHandler = (event: KanbanPrintJob, context, callback) => RedAgate.renderOnAwsLambda(
<Html5>kanban</Html5>, callback);

App.route('/', (evt, ctx, cb) => cb(null, 'Hello, Node!'))
   .route('/billing', billngReportHandler)
   .route('/kanban', kanbanReportHandler)
   .run({});
#!/usr/bin/env python3

import json
import os
import sys

sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/node_modules/red-agate/')
from redagate_lambda import call, LambdaInternalErrorException


if __name__ == '__main__':
    from flask import Flask, abort
    app = Flask(__name__)

    @app.errorhandler(LambdaInternalErrorException)
    def internal_error_handler(e):
        return 'Internal Server Error', 500

    @app.route('/billing')
    def run_billing_report():
        with open('./src/reports/billing.data.json') as f:
            event = json.loads(f.read())
            event['eventName'] = '/billing'
            return call(command=["node", "dist/app.js"], event=event)

    @app.route('/kanban')
    def run_barcode_test_report():
        with open('./src/reports/kanban.data.json') as f:
            event = json.loads(f.read())
            event['eventName'] = '/kanban'
            return call(command=["node", "dist/app.js"], event=event)

    port = int(os.environ['PORT']) if os.environ.get('PORT') is not None else None
    app.run(debug=True, port=port)

Mix react elements:

/** @jsx react.createElement */
import * as react from 'react';

interface ReactHelloProps {
    name: string;
}

export const ReactHello: React.SFC<ReactHelloProps> = (props) => {
    return (<span>Hello, {props.name}!</span>);
};
/** @jsx RedAgate.createElement */
import * as RedAgate          from 'red-agate/modules/red-agate';
import { Html5 }              from 'red-agate/modules/red-agate/html';

import { ReactHost }          from 'red-agate-react-host/modules/react-host';
import { ReactHello }         from './hello';
import { createElement as $ } from 'react';

RedAgate.renderAsHtml(
<Html5>
    <ReactHost element={$(ReactHello, {name: '😎React😎'})} />
</Html5>)
.then(html => console.log(html))
.catch(error => console.log(error))

We provide ES6 module files under red-agate*/modules/* path.
You can get the benefits of tree shaking when using webpack.
Instead, you can also import the whole by simply specifying red-agate* as the import path.

Component Lifecycle

call ordermethoddescription
0earlyConstruct(): voidThis method is marker and it will be NEVER called.
If it defined, constructor will be called in createElement().
Otherwise constructor will be called in render???() APIs.
1constructor(props) /
lambda(props)
Construct a component.
If it is lambda, transform myself and children DOM tree.
2transform(): RedAgateNodeTransform myself and children DOM tree.
This method is equivalent to render() of React method.
3defer(): Promise<any>Wait for asynchronous resources.
4beforeRender(
    contexts: Map<string, any>
): void
Get contexts provided by parent elements.
Preparing something for child elements.
5render(
    contexts: Map<string, any>,
    children: string
): string
Return rendering result as string.
6afterRender(
    contexts: Map<string, any>
): void
Clean up contexts, graphic states, ...

APIs

/** @jsx RedAgate.createElement */
import * as RedAgate from 'red-agate/modules/red-agate'

methoddescription
RedAgate.createElement(
    type: ComponentFactory<P>,
    props: P or null or undefined,
    ...children: RedAgateNode[]
): RedAgateElement<P>
Create a element.
This function is called from JSX compiled code.
RedAgate.renderAsHtml(
    element: RedAgateNode
): Promise<string>
Render elements to string.
RedAgate.render(
    element: RedAgateNode,
    container: HTMLElement,
    callback?: (
        html: string or null,
        error: any or null
    ) => void
): void
Render elements and apply to DOM.
RedAgate.renderOnAwsLambda(
    element: RedAgateNode,
    callback: (
        error: any or null,
        result: any or null
    ) => void
): void
Render elements to string.
Return result via AWS lambda callback.
RedAgate.renderOnExpress(
    element: RedAgateNode,
    req: any,
    res: any
): void
Render elements to string.
Return result via Express web server callback.

import { query } from 'red-agate/modules/red-agate/data'

methoddescription
query(
    data: T[]
): Query<T>
Transform an array.
Query<T>#orderBy(
    condition: Array<string or
    string[
        /* colName: string,
        ('asc' or 'desc') */
    ]> or
        ((a: T, b: T) =>
        number)
): Query<T>
Sort an array.
Query<T>#groupBy(
    condition: string[
        /* colName: string */
    ] or
        ((a: T, b: T,
        index: number, array: T[]) =>
        boolean)
): Query<T[]>
Grouping and transform an array.
Query<T>#groupEvery(
    n: number or
    {
        single: number,
        first?: number,
        intermediate: number,
        last?: number
    }
): Query<T[]>
Grouping and transform an array.
Query<T>#where(
    fn: (
        value: T,
        index: number,
        array: T[]
    ) => boolean
): Query<T>
Filter an array.
Query<T>#select<R>(
    fn?: (
        value: T,
        index: number,
        array: T[]
    ) => R
): Array<R or T>
Map an array.

import { App } from 'red-agate/modules/red-agate/app'

methoddescription
App.cli(
    options: string[]
    handler: (
        opts: Map<string, string>
        ) => void
): App
Add CLI routing.
If options[i] starts with ? it is a optional parameter.
If options[i] ends with * it is a wildcard.
App.route(
    name: string
    lambda: Lambda
): App
Add routing to lambda.
name parameter is used as routing path.
When request event is received call the lambda that name equals to event.eventName.
App.run(
    context: any
    lambda?: Lambda
): App
Run routing.
event is received from stdin as JSON and send response to stdout.
Exit process by calling exit() when response is ended.
If lambda is specified, ignore route() and call lambda.

import { Lambdas } from 'red-agate/modules/red-agate/app'

methoddescription
Lambdas.pipe(
    handler1: Lambda,
    handler2: Lambda
): Lambda
Pipe 2 lambdas.
Return a composite function that piping 2 lambdas.
2nd lambda's event is 1st lambda's callback result.

import { HtmlRenderer } from 'red-agate/modules/red-agate/renderer'

$ npm install puppeteer --save
methoddescription
HtmlRenderer.toPdf(
    html: string or Promise<string>,
    navigateOptions: any,
    pdfOptions: any
): Promise<Buffer>
Render HTML into PDF using puppeteer.
See puppeteer#page.goto about navigateOptions.
See puppeteer#page.pdf about pdfOptions.
HtmlRenderer.toImage(
    html: string or Promise<string>,
    navigateOptions: any,
    imageOptions: any
): Promise<Buffer>
Render HTML into image using puppeteer.
See puppeteer#page.goto about navigateOptions.
See puppeteer#page.screenshot about imageOptions.
HtmlRenderer.toPdfHandler(
    handler: Lambda,
    navigateOptions: any,
    pdfOptions: any
): Lambda
Create composite function returning pdf as callback result.
HtmlRenderer.toImageHandler(
    handler: Lambda,
    navigateOptions: any,
    imageOptions: any
): Lambda
Create composite function returning image as callback result.

Standard Tag-Libs

red-agate/modules/red-agate/taglib

tagdescription
RepeatLoop N times.
ForEachIterate an array.
IfConditional branch.
DoCall a lambda function when createElement .
FacetGrouping child elements.
Give a name to group.
TemplateSynonym for Facet .

red-agate/modules/red-agate/bundler

tagdescription
AssetFetch a external resource.
Fetched resource is referred from other tags.
ImageFetch a external image resource.
ScriptFetch a external script resource.
StyleFetch a external stylesheet resource.
FontSynonym for Style .
SingleFontFetch a external single font-family font resource.

red-agate/modules/red-agate/html

tagdescription
Html4_01_StrictOutput doctype declaration and html tag.
Html4_01_TransitionalOutput doctype declaration and html tag.
Html4_01_FramesetOutput doctype declaration and html tag.
Xhtml1_0_StrictOutput doctype declaration and html tag.
Xhtml1_0_TransitionalOutput doctype declaration and html tag.
Xhtml1_0_FramesetOutput doctype declaration and html tag.
Html5Output doctype declaration and html tag.
XmlOutput xml declaration.
HtmlImpositionImpose pages in a physical page.

red-agate/modules/red-agate/svg

tagdescription
SvgOutput svg tag.
Children can use a Canvas context.
AmbientChange current graphic state properties.
ArcDraw an arc.
CanvasCall a lambda function and draw by using Canvas context object.
CircleDraw a circle.
CurveDraw bezier curve(s).
GridLineDraw grid lines for design time.
GroupGroup children.
Output g tag.
LineDraw line(s).
PathGroup path fragments (e.g. Arc, Circle, Curve, Line, Rect, ...) .
PieDraw a pie.
PolygonDraw a polygon.
RectDraw a rectangle.
RoundRectDraw a rounded rectangle.
SvgAssetFragmentAppend raw SVG tags into defs.
SvgFragmentAppend raw SVG tags.
TextDraw text line(s).
SvgImpositionImpose pages in a physical page.

red-agate/modules/red-agate/printing

tagdescription
PrinterMarksPropsDraw printer marks (crop mark, bleed mark, center mark, fold mark).

red-agate-barcode/modules/barcode/(Code39|Code128|Ean|Itf|JapanPostal|Nw7|Qr)

$ npm install red-agate-barcode --save
tagdescription
Code39Draw a CODE39 barcode.
Code128Draw a CODE128 barcode. (GS1-128 is available)
Ean13Draw a EAN-13 (GTIN-13 / JAN-13) barcode.
Ean8Draw a EAN-8 (GTIN-8 / JAN-8) barcode.
Ean5Draw a EAN-5 (JAN-5) barcode.
Ean2Draw a EAN-2 (JAN-2) barcode.
UpcADraw a UPC-A (GTIN-12) barcode.
UpcEDraw a UPC-E barcode.
ItfDraw a ITF barcode. (GTIN-14 is available)
JapanPostalDraw a Japan Post Customer barcode.
Nw7Draw a NW7 (Codabar) barcode.
QrDraw a QR Code (model 2) barcode.

red-agate-react-host/modules/react-host

$ npm install react --save
$ npm install react-dom --save
$ npm install red-agate-react-host --save
tagdescription
ReactHostHost a react element and render as static markup.

Configurations for building application

If you want to use red-agate w/o jsx pragma comment (/** @jsx RedAgate.createElement */),
You should configure tsconfig or .babelrc for building JSX.
Prease see typescript docs , babel docs or example.

FAQ

  • Can I receive element events (e.g. onclick) ?
    • No. RedAgate is static renderer. Please use React, Vue, Riot, Angular, knockout, ...
  • Can I change DOM via API after rendered to real DOM?
    • No. Please use React, Vue, Riot, Angular, knockout, ...
  • Can I build print preview window by using RedAgate?
    • paper-css may help you to build print previews.

License

ISC
Copyright (c) 2017, Shellyl_N and Authors.

RunKit is a free, in-browser JavaScript dev environment for prototyping Node.js code, with every npm package installed. Sign up to share your code.
Sign Up for Free