Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/frameworks.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,12 @@
{ "type": "build", "runFrequency": 5 },
{ "type": "dependencies" }
]
},
"app": {
"package": "app-mastro",
"buildScript": "build:app-mastro",
"buildOutputDir": "generated",
"measurements": [{ "type": "ssr" }]
}
},
{
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
"dev:solid-start": "pnpm --filter starter-solid-start dev",
"build:solid-start": "pnpm --filter starter-solid-start build",
"build:app-astro": "pnpm --filter app-astro build",
"build:app-mastro": "pnpm --filter app-mastro generate",
"build:app-nuxt": "pnpm --filter app-nuxt build",
"build:app-sveltekit": "pnpm --filter app-sveltekit build",
"build:app-next-js": "pnpm --filter app-next-js build",
Expand Down
3 changes: 3 additions & 0 deletions packages/app-mastro/.vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"recommendations": ["ms-fast.fast-tagged-templates"]
}
40 changes: 40 additions & 0 deletions packages/app-mastro/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# Mastro Template Basic for Node.js

This is a basic TypeScript template for [Mastro](https://mastrojs.github.io) when using [Node.js](https://nodejs.org).

Click the green **Use this template** button in the top right to create your own copy of this repository. Then clone the **Code** to your computer.

## Run locally

If you have multiple projects on your computer that require different Node.js versions, you should install a tool to manage those version for you; for example [Volta](https://volta.sh/) (see [pnpm Support](https://docs.volta.sh/advanced/pnpm)).

Mastro requires Node.js >=24 (unless you want to install a [`URLPattern` polyfill](https://www.npmjs.com/package/urlpattern-polyfill)).

[JSR recommends](https://jsr.io/docs/npm-compatibility#installing-and-using-jsr-packages) to use `pnpm`.

The first time, you need to:

pnpm install

After that, to start the server:

pnpm run start

and open <http://localhost:8000> in your browser.

To generate the whole static site (this will create a `generated` folder):

pnpm run generate

## Next steps

To see how Mastro works, [follow the guide](https://mastrojs.github.io/guide/server-side-components-and-routing/).

To make sure you're using the latest Mastro packages:

pnpm update "@mastrojs/*" --latest

## Deploy to production

- [Deploy static site](https://mastrojs.github.io/guide/deploy/#deploy-static-site-with-ci%2Fcd)
- [Deploy server](https://mastrojs.github.io/guide/deploy/#deploy-server-to-production)
21 changes: 21 additions & 0 deletions packages/app-mastro/components/Layout.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { type Html, html } from '@mastrojs/mastro'

interface Props {
children: Html
title: string
}

export const Layout = (props: Props) => html`
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>${props.title}</title>
<link rel="stylesheet" href="/styles.css" />
</head>
<body>
<h1>${props.title}</h1>
${props.children}
</body>
</html>
`
26 changes: 26 additions & 0 deletions packages/app-mastro/handlers/home.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { testData } from '../../testdata/src/ssr.ts'
import { html, htmlToResponse } from '@mastrojs/mastro'
import { Layout } from '../components/Layout.ts'

const entries = await testData()

export const GET = () =>
htmlToResponse(
Layout({
title: 'Test',
children: html`
<table>
<tbody>
${entries.map(
(entry) => html`
<tr>
<td>${entry.id}</td>
<td>${entry.name}</td>
</tr>
`,
)}
</tbody>
</table>
`,
}),
)
22 changes: 22 additions & 0 deletions packages/app-mastro/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "app-mastro",
"private": true,
"type": "module",
"scripts": {
"dev": "node --watch server.ts",
"generate": "node node_modules/@mastrojs/mastro/src/generator.js",
"type-check": "tsc"
},
"dependencies": {
"@framework-tracker/testdata": "workspace:*",
"@mastrojs/mastro": "jsr:^0",
"@remix-run/node-fetch-server": "^0.11"
},
"devDependencies": {
"@types/node": "^24",
"typescript": "^5"
},
"engines": {
"node": ">=24.12"
}
}
26 changes: 26 additions & 0 deletions packages/app-mastro/routes/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
html {
font-family: sans-serif;
}

h1,
h2,
h3,
h4,
h5,
h6 {
word-break: break-word;
text-wrap-style: pretty;
}

p {
hyphens: auto;
word-break: break-word;
}

img {
max-width: 100%;
}

@view-transition {
navigation: auto;
}
27 changes: 27 additions & 0 deletions packages/app-mastro/server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import * as http from 'node:http'
import { createRequestListener } from '@remix-run/node-fetch-server'
import { Mastro } from '@mastrojs/mastro/server'
import { GET as getHome } from './handlers/home.ts'

// This is use Mastro's programmatic (Express-like) router
// because the default file-based router requires the
// current working directory to be the project root, which it isn't
// always in this pnpm monorepo.

const app = new Mastro<unknown, void>().get('/', getHome)

export const handler = createRequestListener(app.createHandler())

const port = 8000

if (import.meta.main) {
const server = http.createServer(handler)

server.on('error', (e) => {
console.error(e)
})

server.listen(port, () => {
console.log(`Server running at http://localhost:${port}`)
})
}
15 changes: 15 additions & 0 deletions packages/app-mastro/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"compilerOptions": {
"allowImportingTsExtensions": true,
"module": "NodeNext",
"moduleResolution": "nodenext",
"noEmit": true,
"skipLibCheck": true,
"strict": true,
"verbatimModuleSyntax": true,

"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true
}
}
11 changes: 11 additions & 0 deletions packages/stats-generator/src/ssr/handlers/mastro.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { join } from 'node:path'
import { pathToFileURL } from 'node:url'
import { packagesDir } from '../../constants.ts'
import type { SSRHandler } from '../types.ts'

export async function buildMastroHandler(): Promise<SSRHandler> {
const entryPath = join(packagesDir, 'app-mastro', 'server.ts')
const entryUrl = pathToFileURL(entryPath).href
const { handler } = await import(entryUrl)
return handler as SSRHandler
}
7 changes: 7 additions & 0 deletions packages/stats-generator/src/ssr/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { runBenchmark } from './run-benchmark.ts'
import { buildAstroHandler } from './handlers/astro.ts'
import { buildMastroHandler } from './handlers/mastro.ts'
import { buildNuxtHandler } from './handlers/nuxt.ts'
import { buildSvelteKitHandler } from './handlers/sveltekit.ts'
import { buildNextJSHandler } from './handlers/nextjs.ts'
Expand Down Expand Up @@ -27,6 +28,12 @@ const SSR_FRAMEWORKS: SSRFrameworkConfig[] = [
package: 'app-astro',
buildHandler: buildAstroHandler,
},
{
name: 'mastro-ssr',
displayName: 'Mastro SSR',
package: 'app-mastro',
buildHandler: buildMastroHandler,
},
{
name: 'nuxt-ssr',
displayName: 'Nuxt SSR',
Expand Down
19 changes: 19 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.