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 🙏

© 2026 – Pkg Stats / Ryan Hefner

lex-press

v3.1.3

Published

Minimalist framework for HTML and JSX with LEX, built as an Express wrapper with automatic routing and server-side props

Readme

Lex-Press

This is my framework for working with HTML or JSX files with LEX. It follows a minimalist philosophy with full user control. It integrates utility functionalities from frameworks like Next.js but integrated into an Express wrapper.

Note: This framework includes body-parser and cookie-parser configured by default to facilitate request and cookie handling.

Installation

Use npm i lex-press or npx create-lex-press-app

The second option simply adds a small initial configuration.

Initialization

To get started, import lex-press with require and then instantiate an app just like you would in Express:

const lexpress = require("lex-press");

const app = lexpress();

Now you'll have access to all Express methods since this framework only extends Express:

const lexpress = require("lex-press");

const app = lexpress();

app.get("/", (req, res) => res.send("Hello world"));

app.listen(3000, () => {
    console.log("Listening on port 3000");
});

App Tag

The lexpress factory accepts an optional tag parameter to identify the app instance. This is especially useful for libraries that bundle a lexpress app, making them resilient to async or non-deterministic mounting scenarios:

const app = lexpress({ tag: "my-library-app" });

Without a tag, apps are identified by an auto-incrementing counter. When building for production, each tag gets its own folder structure under .lex-press-app/{tag}/.

Public

It's a utility method for serving public directories. You pass it the folder path and you're done:

app.public("public");

Using a custom key

If your path depends on __dirname (e.g., in a library), you should provide a key option. This ensures the path mapping works correctly after bundling:

app.public(__dirname + "/public", { key: "my-library-public" });

Without a key, the path is normalized relative to process.cwd(), which may not work correctly when bundled.

HTML

It's a method that declares a route and an HTML file to be served. In development and build mode, it compiles the HTML before serving it. In production, it expects the build to have already been done:

app.html("/home", "./pages/index.html");

If the HTML includes src or inline scripts, it will be automatically compiled resulting in a single HTML with inline scripts.

The JavaScript bundle supports imports of CSS or module.css files, text files, JSON, and files. So you can import images and get a src. CSS is processed and added to the HTML files inline.

JSX

Similar to app.html but receives route, page, and layout:

app.jsx("/home", "./pages/layout.jsx", "./pages/page.jsx");

Layout must export a Layout component and Page must export a Page component:

const Layout = ({ children }) =>
{
    return <html>
        <head>
            <title>My App</title>
        </head>
        <body>
            {children}
        </body>    
    </html>;
};
export default Layout;
const Page = () =>
{
    return <h1>Hello world</h1>;
};
export default Page;

Views

The views method declares an automatic routing folder in the style of Vite or Next.js:

app.views("./views");
views/
├── layout.jsx
├── home/
│   └── page.jsx
├── about/
│   └── page.html
└── contact/
    └── page.jsx

A schema like this would serve the page elements.

  • If we have page.html, it's served directly
  • If it's .jsx, the closest layout is used by going back through the directories

Using a custom key

If your views path depends on __dirname (e.g., in a library that needs to locate its own view files after bundling), you must provide a key option:

app.views(__dirname + "/views", { key: "my-library-views" });

Without a key, the path is normalized relative to process.cwd(), which may not work correctly when bundled.

Important

The html, jsx, and views methods are not dynamic, so after listen they will no longer have an effect. This is because in production there is no build for security reasons.

If you want to create or remove routes after listen, you must do it as you would with Express.

Sub-apps

You can mount a lexpress instance inside another with app.use(), just like Express. The sub-app can be mounted on the same route (app.use(admin)) or with a base-url (app.use("/admin", admin)):

const lexpress = require("lex-press");

const app = lexpress();
const admin = lexpress();

app.views("./views");
admin.views("./views-admin");

// admin lives under /admin
app.use("/admin", admin);

In the development server, the HTML of a sub-app with base-url adapts automatically: its assets (/__assets/...) and the hot-reload script are served with the base prefix (/admin/__assets/...).

In production, mount each app on a single base (see the Production section).

Development

The default mode of the framework is development, so you can run the development server simply with:

node index.js

or with nodemon for hot-reload

nodemon --watch ./ --ext js,jsx,ts,tsx,json,html,css --exec "node index.js"

Build

To compile the server, all you need to do is add the --build flag to the main process:

node index.js --build

This will compile the production server into .lex-press-app/server.js.

If your project uses non-bundleable libraries (whether because they use binaries or any other reason), we can pass a list of external dependencies:

node index.js --build --external external-module1 external-module2

You can also use a series of esbuild flags if you need to customize the export:

  • build
  • format
  • platform
  • target
  • bundle
  • minify
  • sourcemap
  • treeShaking
  • external
  • tsconfig

Important about __dirname

When bundling, __dirname in your code will resolve to the .lex-press-app/ directory, not your original project directory. However, __dirname is a valid option for resolving paths to views or public if you provide a key option to those methods:

app.views(__dirname + "/views", { key: "my-views" });
app.public(__dirname + "/public", { key: "my-public" });

For other routes and file resolutions, use process.cwd() or an environment variable instead.

Production

Once bundled, you can run your server from the .lex-press-app/server.js file:

node .lex-press-app/server.js

The .lex-press-app folder also contains compiled HTML files, assets, and copies of public folders you've added to the project with app.public(). Therefore, this folder alone can contain everything necessary for a deployment that only uses app.public() and app.views().

The server file should only be executed from outside the folder.

Sub-apps and base-url

Each app's compiled HTML is escaped only once, with the base of the first request it receives (the tree is static). If you mount the same instance on two bases, only the first one will serve HTML with correct asset paths: mount each app on a single base.

About Express middleware

Lexpress exposes all the properties of the Express function, so you simply need to call app.use(lexpress.json()) to access this functionality, just as you would in Express.