Northern Pileated Marmoset

    pragmatic-view
    TypeScript icon, indicating that this package has built-in type declarations

    1.0.0 • Public • Published

    Under development. Current build might be unstable.

    SymfonyInsight

    npm node npm Coverage Status Gitlab pipeline status (branch)

    PragmaticView

    PragmaticView

    JSX and TSX template engine for server-side Node.JS apps inspired by React and by .NET platform.

    Typed for Typescript

    npm install --save pragmatic-view
    

    Why?

    React brought awesome JSX syntax. JSX has become one of the most convenient ways to write HTML-like syntax in JavaScript. Due to it's deep interconnection with React, JSX has never become standalone language feature. This framework borrows React's JSX regarding the syntax while providing own functionality and features. Some part are inspired by ASP.NET. Eventually this framework will provide more ASP.NET like features such as server-side handlers.

    Getting started

    Checkout documentation

    PragmaticView is usable together with transpiler like Babel or TSC (even TS-NODE and babel/register). These transpilers are set to work with React by default. In order to make PragmaticView a new pragma has to be set. Pragma is a function that JSX is transpiled to. In TypeScript it's called jsxFactory. PragmaticView also provides support for custom file extension ".pv" in order to be distinguished from JSX files created for React. Using ".pv" it's not mandatory and usually it's easier to write views in ".jsx" or ".tsx" files.

    // With Babel (for TSC checkout documentation)
    {
      "plugins": [
        ["@babel/plugin-transform-react-jsx", {
          "pragma": "View", // <- PragmaticView's pragma
        }]
      ]
    }
    
    // Standalone - place register method before any template imports
    ViewEngine.register(path.resolve(__dirname, './templates'));
    
    import Document from "./templates/document.jsx";
    

    PragmaticView can be used without any transpiler too. It utilizes the same trick that TS-NODE or Babel register does. ViewEngine class offers static method that adds a hook to Node's require method. This hook takes care of transpilation whenever a file is imported. In order not to confuse JSX meant for PragmaticView with JSX meant for React the method takes path as an argument. Only files imported form given path (and sub folders) are transpiled by PragmaticView.

    // ES6
    import { View } from from "pragmatic-view";
    // CommonJS
    const View = require("pragmatic-view").View;
    
    /* Content */
    
    

    Similarly to React, pragma function has to be always imported in the file where components are declared and used. React uses name "React" for it's pragma whereas PragmaticView uses name "View".

    Main Parts

    PragmaticView consists of four major parts: ViewEngine, ViewBuilders, Components.

    ViewEngine is the heart of the application. It holds configuration, caches and other features (coming soon). The main objective of ViewEngine is to generate ViewEngines on demand. There is usually one ViewEngine per application (however, there is no limit).

    // ES6
    import { ViewEngine } from "pragmatic-view";
    // CommonJS
    const ViewEngine = require("pragmatic-view").ViewEngine;
    
    let viewEngine = new ViewEngine();
    

    ViewBuilders are used to generate HTML strings from components. There are generated by ViewEngine for each rendering (usually one per http request). ViewEngine has method dedicated to generating instances of ViewBuilders. They inherit config and/or layout components from parent ViewEngine. Layouts can be changed specifically on the instance of ViewBuilder thus overriding inherited one. ViewBuilder also consumes context object that contains values that will be used by components. Context is exposed to every single component as it's second argument (first argument is props).

    // ES6
    import { View } from from "pragmatic-view";
    // CommonJS
    const View = require("pragmatic-view").View;
    
    const MyDocument = (props, context) => <div>Hello world!</div>;
    
    let context = {
        path: "/foo/bar"
    };
    
    let viewBuilder = viewEngine.getBuilder(); // Contextless
    let viewBuilder = viewEngine.getBuilder(context); // Contextwise
    viewBuilder.root = MyDocument; // Passing root component after initialization
    
    let viewBuilder = viewEngine.getBuilder(MyDocument); // Contextless
    let viewBuilder = viewEngine.getBuilder(MyDocument, context); // Contextwise
    

    As soon as ViewBuilder is fed all necessary data .toString() method can be invoked. String in promise is returned. Promises are returned as Class Component can contain async function that needs to be awaited.

    // Inside async function
    let html = await viewBuilder.toString();
    console.log(html);
    
    // Outside async function
    viewBuilder.toString().then(html => console.log(html));
    
    // logs: '<div>Hello World!</div>'
    

    ViewEngine options

    {
        beautifyOutput?: boolean,
        beautifyOptions?: HTMLBeautifyOptions,
        logger?: (message: string) => void,
        logRenderTime?: boolean,
        layout?: Component
    }
    

    More about options

    Components

    More about Components

    Function Component

    Very simple component that contains no advanced logic.

    // MyDocument.tsx
    import { View } from from "pragmatic-view";
    import BreadCrumbs from "./BreadCrumbs";
    
    export const MyDocument = (props, context) => {
        return (
            <html>
                <head>
                    <title>My awesome page!</title>
                </head>
                <body>
                    <BreadCrumbs className="bread-crumbs" />
                    <h1>Welcome to my awesome page!</h1>
                </body>
            </html>
        );
    }
    
    

    Class Component

    Powerful building block that can perform async operations such as database calls.

    // BreadCrumbs.tsx
    import { View } from from "pragmatic-view";
    
    export class BreadCrumbs extends View.Component<{ className?: string }, { path: string }> {
    
        private slugs: string[];
    
        async onInit() {
            this.slugs = this.context.path.split('/');
            console.log('I am at path ' + this.context.path);
        }
    
        render() {
            return (
                <div className={this.props.className}>
                    {this.slugs.map(slug => <span>{slug}</span>)}
                </div>
            )
        }
    
        async onRender(html: string) {
            return '<section>' + html + '</section>';
        }
    }
    

    Exotic Components

    Exotic components are helpers exposed on View object.

    Fragment

    Fragments are quite similar to React's. They serve as wrappers around multiple sibling components without creating any additional markup.

    import { View } from "pragmatic-view";
    
    let Comp = () => {
        return (
            <View.Fragment>
                <h1>Inside fragment!</h1>
                <p>Fragment does not render in any kind of tag!</p>
            </View.Fragment>
        );
    }
    

    Layouts & Placeholders

    Layouts are components that contain Placeholder component. Layouts are passed to ViewEngine or ViewBuilder as additional property. Placeholder component is replaced by rendered HTML of the root component.

    import { View } from "pragmatic-view";
    
    let layout = () => <html>
        <head>
            <title>Awesome layout!</title>
        </head>
        <body>
            <View.Placeholder/>
        </body>
    </html>
    
    // In ViewEngine
    let config = {
        layout: layout
    };
    
    let viewEngien = new ViewEngine(config);
    
    // In ViewBuilder
    let viewBuilder = viewEngine.getBuilder(pageComponent);
    viewBuilder.layout = layout;
    

    More about layouts

    Install

    npm i pragmatic-view

    DownloadsWeekly Downloads

    6

    Version

    1.0.0

    License

    MIT

    Unpacked Size

    194 kB

    Total Files

    239

    Last publish

    Collaborators

    • deathrage