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

pragmatic-view

v1.0.0

Published

JSX and TSX templating engine for node applications inspired by React and .NET

Downloads

105

Readme

Under development. Current build might be unstable.

SymfonyInsight

npm node npm Coverage Status Gitlab pipeline status (branch)

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