diff --git a/.github/frameworks.json b/.github/frameworks.json index e93b25a..24fb97a 100644 --- a/.github/frameworks.json +++ b/.github/frameworks.json @@ -145,6 +145,12 @@ { "type": "build", "runFrequency": 5 }, { "type": "dependencies" } ] + }, + "app": { + "package": "app-mastro", + "buildScript": "build:app-mastro", + "buildOutputDir": "generated", + "measurements": [{ "type": "ssr" }] } }, { diff --git a/package.json b/package.json index 873a6c2..bb86143 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/packages/app-mastro/.vscode/extensions.json b/packages/app-mastro/.vscode/extensions.json new file mode 100644 index 0000000..74a66e8 --- /dev/null +++ b/packages/app-mastro/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["ms-fast.fast-tagged-templates"] +} diff --git a/packages/app-mastro/README.md b/packages/app-mastro/README.md new file mode 100644 index 0000000..ecd9935 --- /dev/null +++ b/packages/app-mastro/README.md @@ -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 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) diff --git a/packages/app-mastro/components/Layout.ts b/packages/app-mastro/components/Layout.ts new file mode 100644 index 0000000..f9ae3c2 --- /dev/null +++ b/packages/app-mastro/components/Layout.ts @@ -0,0 +1,21 @@ +import { type Html, html } from '@mastrojs/mastro' + +interface Props { + children: Html + title: string +} + +export const Layout = (props: Props) => html` + + + + + ${props.title} + + + +

${props.title}

+ ${props.children} + + +` diff --git a/packages/app-mastro/handlers/home.ts b/packages/app-mastro/handlers/home.ts new file mode 100644 index 0000000..a72b379 --- /dev/null +++ b/packages/app-mastro/handlers/home.ts @@ -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` + + + ${entries.map( + (entry) => html` + + + + + `, + )} + +
${entry.id}${entry.name}
+ `, + }), + ) diff --git a/packages/app-mastro/package.json b/packages/app-mastro/package.json new file mode 100644 index 0000000..b356513 --- /dev/null +++ b/packages/app-mastro/package.json @@ -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" + } +} diff --git a/packages/app-mastro/routes/styles.css b/packages/app-mastro/routes/styles.css new file mode 100644 index 0000000..efa12c4 --- /dev/null +++ b/packages/app-mastro/routes/styles.css @@ -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; +} diff --git a/packages/app-mastro/server.ts b/packages/app-mastro/server.ts new file mode 100644 index 0000000..555cfb1 --- /dev/null +++ b/packages/app-mastro/server.ts @@ -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().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}`) + }) +} diff --git a/packages/app-mastro/tsconfig.json b/packages/app-mastro/tsconfig.json new file mode 100644 index 0000000..826c1a4 --- /dev/null +++ b/packages/app-mastro/tsconfig.json @@ -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 + } +} diff --git a/packages/stats-generator/src/ssr/handlers/mastro.ts b/packages/stats-generator/src/ssr/handlers/mastro.ts new file mode 100644 index 0000000..d8ec9e1 --- /dev/null +++ b/packages/stats-generator/src/ssr/handlers/mastro.ts @@ -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 { + const entryPath = join(packagesDir, 'app-mastro', 'server.ts') + const entryUrl = pathToFileURL(entryPath).href + const { handler } = await import(entryUrl) + return handler as SSRHandler +} diff --git a/packages/stats-generator/src/ssr/index.ts b/packages/stats-generator/src/ssr/index.ts index d1846b5..8eaf46b 100644 --- a/packages/stats-generator/src/ssr/index.ts +++ b/packages/stats-generator/src/ssr/index.ts @@ -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' @@ -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', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index acf2186..1f78ae9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,6 +39,25 @@ importers: specifier: ^5.16.15 version: 5.16.15(@types/node@25.0.6)(db0@0.3.4)(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.2)(rollup@4.54.0)(terser@5.44.1)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.8.2) + packages/app-mastro: + dependencies: + '@framework-tracker/testdata': + specifier: workspace:* + version: link:../testdata + '@mastrojs/mastro': + specifier: jsr:^0 + version: '@jsr/mastrojs__mastro@0.6.4' + '@remix-run/node-fetch-server': + specifier: ^0.11 + version: 0.11.0 + devDependencies: + '@types/node': + specifier: ^24 + version: 24.10.13 + typescript: + specifier: ^5 + version: 5.9.3 + packages/app-next-js: dependencies: '@framework-tracker/testdata':