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

express-engine-jsx

v3.7.0

Published

JSX engine for ExpressJS

Downloads

201

Readme

express-engine-jsx

Full-featured template engine for express

Build Status

Example of users.jsx template file

const Layout = require('./layout');

<Layout>
  <ul class="users">
    {users.map(user => (
      <li key={user}>{user.name}</li>
    ))}
  </ul>
</Layout>

Example of layout.jsx template file

<html lang={lang}>
<head>
  <meta charset="UTF-8"/>
</head>
<body>{children}</body>
</html>

Example of router

app.get('/users', function (req, res) {
  res.locals.lang = 'en';
  res.render('users', {
    users: [
      {name: 'Max'},
      {name: 'Bob'}
    ]
  });
});

Output html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"/></head>
<body><ul class="users"><li>Max</li><li>Bob</li></ul></body>
</html>

How it works

When you render some template, this engine takes jsx file like this

const Layout = require('./layout');

<Layout>
  <ul class="users">
    {users.map((user, i) => (
      <li key={i}>{user.name}</li>
    ))}
  </ul>
</Layout>

and compiles it to js file like this

const React = require('react');
const requireJSX = require('express-engine-jsx/require');
const Context = require('express-engine-jsx/Context');

module.exports = function (props) {
  const __components = [];
  const context = React.useContext(EngineContext);
  const locals = context.locals || {};
 
  with (locals) {
    with (props) {
      const Layout = requireJSX('./layout');

      __components.push(
        React.createElement(
          Layout, 
          null,
          React.createElement(
            'ul',
            {className: 'users'},
            users.map((user, i) => (
              React.createElement(
                'li',
                {key: i},
                user.name
              )
            ))
          )
        )
      );
    }
  }
  
  return __components;
};

and now this component can be rendered to html with ReactDOM.renderToStaticMarkup().

As you can see, each jsx template file returns array of components and standard html attributes are converted to react attributes

<div class="first" tabindex="1"></div>
<div class="second" tabindex="2"></div>
//...
__components.push(React.createElement('div', {className: 'first', tabIndex: '1'}));
__components.push(React.createElement('div', {className: 'second', tabIndex: '2'}));
//...

return __components;

Usage

npm i express-engine-jsx react react-dom

react and react-dom are peer dependencies in this package

const express = require('express');
const app = express();
const engine = require('express-engine-jsx');

server.set('views', './path/to/views');
server.set('view engine', 'jsx');
server.engine('jsx', engine);

// optionaly
engine.setOptions({
  // See options section
});

API

engine

const engine = require('express-engine-jsx');

It's a function which takes three arguments:

  • path - path to jsx file
  • props - object with properties which will be local variables in jsx file. locals property will be visible for all deep components. Properties of locals object will be as local variables in all deep components.
  • options - optional, object which overwrites any of next three global options doctype, replace, renderer
  • callback - optional, Node style callback which will receive result of options.renderer as second argument

If you pass to engine arguments without callback then it will return result of renderer of global options.

engine('/path/to/view', {prop: 'value'}, (err, html) => console.log(html));

const html = engine('/path/to/view', {prop: 'value'});

Also, it has method engine.setOptions(options) which can modify global options

options

const options = require('express-engine-jsx/options');

Object with optional properties:

  • DEV - boolean, default process.env.NODE_ENV !== 'production'
  • sourceMap - boolean, default process.env.NODE_ENV !== 'production'
  • doctype - string which will be prepended to output html, default value is "<!DOCTYPE html>\n"
  • renderer - function, default ReactDOM.renderToStaticMarkup
  • replace - function which will take output html (without doctype), and it should return new html
  • addOnChange - boolean, default true. Will add onChnage={() => false} to every <input> with value or checked attribute. Used to omit ReactDOM warning about value prop without onChange handler.
  • templatePath - path to wrapper of compiled jsx, default value is express-engine-jsx/template.jsx. Undefined variable BODY will be replaced with your compiled jsx code.
  • parserOptions - options for babel.parser
  • templateOptions - options for babel.template

require

const requireJSX = engine.require || require('express-engine-jsx/require');

This is a function which you can use as regular require but this one can run jsx files. It checks if path is jsx file and if it is then requireJSX will convert this file to js code and then run it.

It also can take optional second parameter - currentWorkingDir which should be an absolute path to file directory which calls require in case when you call require from some unusual place like debugger console.

Every compiled jsx file will be cached to requireJSX.cache object where key will be path to jsx file without extension and value will be object {moduleExports: ReactComponent|any, map: object|null}. You can delete any key in this cache, requireJSX will recompile jsx file on next call.

convert

const convert = engine.convert || require('express-engine-jsx/convert');

It is a function which can convert jsx template code to js code.

Arguments:

If you pass sourceMap: true or your process.env.NODE_ENV !== 'production' then convert will return object {code: string, map: object} instead of js code string.

It also has convert.cache object for compiled templates where keys are templatePath and values are functions created by babel.template

run

const run = engine.run || require('express-engine-jsx/run');

Function which can execute js code with independent context and returns result of module.exports inside js code.

Arguments:

  • code - string of js code
  • options
    • path - string, path to file, usually path to jsx file
    • context - object which properties will be global variables inside js code
    • scriptOptions - object options for vm.Script

Context

const Context = engine.Context || require('express-engine-jsx/Context');

React context which used to bypass locals to components

attr-map

const attrMap = require('express-engine-jsx/attr-map');

This is an object where keys are names of html attributes in lower case like class and values are valid React html attributes like className. You can modify this object if I forget about some attributes.

How to integrate to other engine

For example how to integrate to ejs

const express = require('express');
const app = express();
const engine = require('express-engine-jsx');
const {dirname, resolve} = require('path');

app.locals.component = function (path, props = {}) {
  props = Object.assign({}, this, props);

  return engine(resolve(dirname(this.filename), path), props);
};

Now we can use component() in ejs files like this

<div><%- component('path/to/jsx-view', {prop: 'value'}) %></div>

Problem with more than one component in template root

In javascript you can omit ; and write like this

"first"
"second"

It does nothing, but it's valid code. In JSX you can't do same thing with elements

<div>first</div>
<div>second</div>

It will throw compilation error. It's waiting for ; after first element. You have three options to solve this problem.

First - use ;

<div>first</div>;
<div>second</div>;

Second - use <Fragment>

<Fragment>
    <div>first</div>
    <div>second</div>
</Fragment>

Third - use short Fragment notation <>...</>

<>
    <div>first</div>
    <div>second</div>
</>

License

MIT, see LICENSE file