Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

MoroJS migrations

Small tooling to move existing Express code toward MoroJS (@morojs/moro) with predictable, mechanical rewrites.

Verified against @morojs/moro 1.8.3.

What’s here

Path Purpose
express/scripts/migrate-express.mjs Walks a directory tree, rewrites Express imports and the usual factory/middleware helpers to MoroJS equivalents, and reports what it could not translate.

Usage

From the repo root (or anywhere), run Node against the script and pass the root of the codebase you want to scan:

node express/scripts/migrate-express.mjs /path/to/your/app           # dry-run: prints diffs, no writes
node express/scripts/migrate-express.mjs /path/to/your/app --write   # apply changes in place

Optional:

  • --ext=ts,js — comma-separated extensions (default: ts, tsx, js, mjs, cjs)

The script skips node_modules, dist, build, .git, .next, and coverage.

Rewrites

Express MoroJS
require('express') / from 'express' named imports from @morojs/moro
express() await createApp()
express.Router() createRouter()
express.json(...) json(...)
express.urlencoded(...) urlencoded(...)
express.static(dir) middleware.staticFiles({ root: dir })
app.use('/p', express.static(dir)) app.use(middleware.staticFiles({ root: dir, prefix: '/p' })) — needs @morojs/moro >= 1.8.8
Request / Response (types) HttpRequest / HttpResponse (aliased on import)

The import statement is rebuilt, not just re-pointed

This is the part that has to be right or the output does not run:

  • createApp() is async. It returns Promise<Moro>, so it must be awaited — otherwise app is a Promise and every app.use / app.get / app.listen throws.
  • @morojs/moro has no default export. Leaving import express from '@morojs/moro' in place is a hard ESM error, so the default binding is dropped.
  • The names the rewrite introduces have to be bound. createApp, createRouter, json, urlencoded, and middleware are collected per file and emitted as a single import/require.
// before
const express = require('express');
const app = express();
app.use(express.json());
const router = express.Router();

// after
const { middleware, createApp, createRouter, json } = require('@morojs/moro');
const app = await createApp();
app.use(json());
const router = createRouter();

Reported, not rewritten

These have no mechanical equivalent in MoroJS 1.8.3. The script prints a “Needs manual work” report with file and line rather than emitting code that looks right and silently misbehaves.

Pattern Why
app.use('/prefix', middlewareFn) MoroJS only honours a path prefix when argument 2 is a createRouter() instance. Anything else is a silent no-op — no error, no warning, the middleware simply never runs. (express.static() at a prefix is the one exception: it is rewritten, into staticFiles' prefix option.)
express.static(dir, { maxAge }) Express’s maxAge is milliseconds; MoroJS writes the value straight into Cache-Control: max-age=, which is seconds.
Wildcard route paths ('/files/*') MoroJS’s path matcher never compiles * to a wildcard — it survives into the regex as a quantifier, so /files/* matches /files/ but not /files/a.txt.
app.set() / app.locals / app.engine() No equivalent. Use createApp(options) or moro.config.js, and app.decorate() / res.locals.
res.render() Only exists once middleware.template({...}) is installed, and MoroJS ships its own minimal template syntax — it does not run Pug/EJS/Handlebars.
NextFunction Not exported. MoroJS types next as () => void; see the Middleware type.
Top-level await createApp() in CommonJS Top-level await is ESM-only. Wrap the module body in an async IIFE or convert the file to ESM.

Already compatible, nothing to rewrite: app.listen() callback style, 4-arg error middleware, next(err), and the res.status / res.json / res.send / res.cookie / res.redirect surface.

Limitations

The codemod only performs the swaps in the table above. It does not rewrite arbitrary middleware or routing APIs. Aliases like import { Router as R } from 'express' get the import source updated; local names stay as you wrote them. Always dry-run and review the diff first, especially in projects with custom wrappers around Express.

Tests

npm install
npm test

express/tests/migrate-express.test.ts runs the script as a subprocess against temp fixtures and asserts against the real @morojs/moro 1.8.3 API surface.

About

Tools to migrate from existing projects to MoroJS

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages