diff --git a/.github/frameworks.json b/.github/frameworks.json index 8b4f416..a326cb5 100644 --- a/.github/frameworks.json +++ b/.github/frameworks.json @@ -125,6 +125,22 @@ "measurements": [{ "type": "ssr" }] } }, + { + "name": "mastro", + "displayName": "Mastro", + "frameworkPackage": "jsr:@mastrojs/mastro", + "focusedFramework": false, + "starter": { + "package": "starter-mastro", + "buildScript": "generate", + "buildOutputDir": "generated", + "measurements": [ + { "type": "install", "runFrequency": 5 }, + { "type": "build", "runFrequency": 5 }, + { "type": "dependencies" } + ] + } + }, { "name": "solid-start", "displayName": "SolidStart", diff --git a/package.json b/package.json index 55f8b77..873a6c2 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,8 @@ "build:sveltekit": "pnpm --filter starter-sveltekit build", "dev:astro": "pnpm --filter starter-astro dev", "build:astro": "pnpm --filter starter-astro build", + "dev:mastro": "pnpm --filter starter-mastro dev", + "build:mastro": "pnpm --filter starter-mastro build", "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", diff --git a/packages/starter-mastro/.vscode/extensions.json b/packages/starter-mastro/.vscode/extensions.json new file mode 100644 index 0000000..74a66e8 --- /dev/null +++ b/packages/starter-mastro/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["ms-fast.fast-tagged-templates"] +} diff --git a/packages/starter-mastro/README.md b/packages/starter-mastro/README.md new file mode 100644 index 0000000..ecd9935 --- /dev/null +++ b/packages/starter-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/starter-mastro/components/Layout.ts b/packages/starter-mastro/components/Layout.ts new file mode 100644 index 0000000..f9ae3c2 --- /dev/null +++ b/packages/starter-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/starter-mastro/package.json b/packages/starter-mastro/package.json new file mode 100644 index 0000000..1a0755f --- /dev/null +++ b/packages/starter-mastro/package.json @@ -0,0 +1,20 @@ +{ + "name": "starter-mastro", + "type": "module", + "scripts": { + "dev": "node --watch server.ts", + "build": "node node_modules/@mastrojs/mastro/src/generator.js", + "check": "tsc" + }, + "dependencies": { + "@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/starter-mastro/routes/index.server.ts b/packages/starter-mastro/routes/index.server.ts new file mode 100644 index 0000000..77e5de5 --- /dev/null +++ b/packages/starter-mastro/routes/index.server.ts @@ -0,0 +1,10 @@ +import { html, htmlToResponse } from '@mastrojs/mastro' +import { Layout } from '../components/Layout.ts' + +export const GET = (_req: Request) => + htmlToResponse( + Layout({ + title: 'Hello World', + children: html`

Welcome!

`, + }), + ) diff --git a/packages/starter-mastro/routes/styles.css b/packages/starter-mastro/routes/styles.css new file mode 100644 index 0000000..efa12c4 --- /dev/null +++ b/packages/starter-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/starter-mastro/server.ts b/packages/starter-mastro/server.ts new file mode 100644 index 0000000..d935388 --- /dev/null +++ b/packages/starter-mastro/server.ts @@ -0,0 +1,15 @@ +import * as http from 'node:http' +import { createRequestListener } from '@remix-run/node-fetch-server' +import mastro from '@mastrojs/mastro/server' + +const port = 8000 + +const server = http.createServer(createRequestListener(mastro.fetch)) + +server.on('error', (e) => { + console.error(e) +}) + +server.listen(port, () => { + console.log(`Server running at http://localhost:${port}`) +}) diff --git a/packages/starter-mastro/tsconfig.json b/packages/starter-mastro/tsconfig.json new file mode 100644 index 0000000..826c1a4 --- /dev/null +++ b/packages/starter-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/pnpm-lock.yaml b/pnpm-lock.yaml index e5507f9..13525c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -228,6 +228,22 @@ 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/starter-mastro: + dependencies: + '@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/starter-next-js: dependencies: next: @@ -1336,6 +1352,45 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jsr/mastrojs__mastro@0.6.4': + resolution: {integrity: sha512-w1YnGd++Ev3xq/E6Lzy8mtNKCMBoe2dBxpv4AjNJsdAr2XqFlX0z74Tno3UmuYZ7Ijzmr4YnOK/iEAGe1L3NEg==, tarball: https://npm.jsr.io/~/11/@jsr/mastrojs__mastro/0.6.4.tgz} + + '@jsr/std__bytes@1.0.6': + resolution: {integrity: sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA==, tarball: https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz} + + '@jsr/std__cli@1.0.27': + resolution: {integrity: sha512-aaY6VYkdv0qmAIiaYNfBH9Pd3Te5bJsEUQmNg/ak43AorET5+pBcI9RgqCgBXfkb2tBnLUlTBklQvirMz/CnAQ==, tarball: https://npm.jsr.io/~/11/@jsr/std__cli/1.0.27.tgz} + + '@jsr/std__encoding@1.0.10': + resolution: {integrity: sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw==, tarball: https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz} + + '@jsr/std__fmt@1.0.9': + resolution: {integrity: sha512-YFJJMozmORj2K91c5J9opWeh0VUwrd+Mwb7Pr0FkVCAKVLu2UhT4LyvJqWiyUT+eF+MdfqQ9F7RtQj4bXn9Smw==, tarball: https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.9.tgz} + + '@jsr/std__fs@1.0.22': + resolution: {integrity: sha512-PvDtgT25IqhFEX2LjQI0aTz/Wg61jCtJ8l19fE9MUSvSmtw57Kzr6sM7GcCsSrsZEdQ7wjLfXvvhy8irta4Zww==, tarball: https://npm.jsr.io/~/11/@jsr/std__fs/1.0.22.tgz} + + '@jsr/std__html@1.0.5': + resolution: {integrity: sha512-8ypLaw6ORY7jisEvsXOS/D631/pMCX78mV7fyromfzJXxqb35OUNCBC2E4Ca0goKQJW8I2XhEgoFu0ZXaIiGvA==, tarball: https://npm.jsr.io/~/11/@jsr/std__html/1.0.5.tgz} + + '@jsr/std__http@1.0.24': + resolution: {integrity: sha512-mfUI8vAkMVvf0wYxkZd9ZKfwFryLanHe+nbvxfFPkNO24B2IY6knkBJNN28cTZ8SITh8t8rv56Cx5uOAc0uGFg==, tarball: https://npm.jsr.io/~/11/@jsr/std__http/1.0.24.tgz} + + '@jsr/std__internal@1.0.12': + resolution: {integrity: sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA==, tarball: https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz} + + '@jsr/std__media-types@1.1.0': + resolution: {integrity: sha512-dHvaxHL7ENWnltgL653uo3KnKFse3ZbopZop2gqsT7yrscx7irZEClu5Cba7gMPPRk4Lg1FbriNcaBViM2RSBw==, tarball: https://npm.jsr.io/~/11/@jsr/std__media-types/1.1.0.tgz} + + '@jsr/std__net@1.0.6': + resolution: {integrity: sha512-mh27Fw4UMCjGSIMoOhjia5cS5fNP9M9DZYhGB7EYSZNnzf/eguFiarii/W4oDwYMmnxCMouUzhc6Y7jFuwTzcg==, tarball: https://npm.jsr.io/~/11/@jsr/std__net/1.0.6.tgz} + + '@jsr/std__path@1.1.4': + resolution: {integrity: sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA==, tarball: https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz} + + '@jsr/std__streams@1.0.17': + resolution: {integrity: sha512-LnPlWk20mDIV5/nqoUomAB8umOimfGEyWRApxLgekXFuqKGDsGpUAi58amieVU2XAGNclmUOtQOcQ/qOl3PNFg==, tarball: https://npm.jsr.io/~/11/@jsr/std__streams/1.0.17.tgz} + '@kwsites/file-exists@1.1.1': resolution: {integrity: sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==} @@ -2177,6 +2232,9 @@ packages: peerDependencies: react-router: 7.10.1 + '@remix-run/node-fetch-server@0.11.0': + resolution: {integrity: sha512-nCrFHVxDFioSHc0g/3m5ztwgjBt7g8qh/UwmYkDjuMePKFepMKfNGgH5S6L7iXKX+jUrf3ooVmhx3NGIoa9iYA==} + '@remix-run/node-fetch-server@0.9.0': resolution: {integrity: sha512-SoLMv7dbH+njWzXnOY6fI08dFMI5+/dQ+vY3n8RnnbdG7MdJEgiP28Xj/xWlnRnED/aB6SFw56Zop+LbmaaKqA==} @@ -2918,6 +2976,9 @@ packages: '@types/node@22.19.3': resolution: {integrity: sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==} + '@types/node@24.10.13': + resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==} + '@types/node@25.0.3': resolution: {integrity: sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==} @@ -6994,6 +7055,10 @@ packages: peerDependencies: typescript: '>=4.8.4' + ts-blank-space@0.6.1: + resolution: {integrity: sha512-LcM3W5HEyzTaXUeQITV8ploUOGe+zuuoFYsCfPscFLhx3bZn2sSfHMKxsULVG/zA7an9UhReiHv4Kk/6QzlpXQ==} + engines: {node: '>=18.0.0'} + tsconfck@3.1.6: resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==} engines: {node: ^18 || >=20} @@ -7063,6 +7128,11 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <6.0.0' + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + typescript@5.9.3: resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} engines: {node: '>=14.17'} @@ -8782,6 +8852,57 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jsr/mastrojs__mastro@0.6.4': + dependencies: + '@jsr/std__http': 1.0.24 + '@jsr/std__media-types': 1.1.0 + '@jsr/std__path': 1.1.4 + ts-blank-space: 0.6.1 + + '@jsr/std__bytes@1.0.6': {} + + '@jsr/std__cli@1.0.27': + dependencies: + '@jsr/std__fmt': 1.0.9 + '@jsr/std__internal': 1.0.12 + + '@jsr/std__encoding@1.0.10': {} + + '@jsr/std__fmt@1.0.9': {} + + '@jsr/std__fs@1.0.22': + dependencies: + '@jsr/std__internal': 1.0.12 + '@jsr/std__path': 1.1.4 + + '@jsr/std__html@1.0.5': {} + + '@jsr/std__http@1.0.24': + dependencies: + '@jsr/std__cli': 1.0.27 + '@jsr/std__encoding': 1.0.10 + '@jsr/std__fmt': 1.0.9 + '@jsr/std__fs': 1.0.22 + '@jsr/std__html': 1.0.5 + '@jsr/std__media-types': 1.1.0 + '@jsr/std__net': 1.0.6 + '@jsr/std__path': 1.1.4 + '@jsr/std__streams': 1.0.17 + + '@jsr/std__internal@1.0.12': {} + + '@jsr/std__media-types@1.1.0': {} + + '@jsr/std__net@1.0.6': {} + + '@jsr/std__path@1.1.4': + dependencies: + '@jsr/std__internal': 1.0.12 + + '@jsr/std__streams@1.0.17': + dependencies: + '@jsr/std__bytes': 1.0.6 + '@kwsites/file-exists@1.1.1': dependencies: debug: 4.4.3 @@ -9614,6 +9735,8 @@ snapshots: - supports-color - typescript + '@remix-run/node-fetch-server@0.11.0': {} + '@remix-run/node-fetch-server@0.9.0': {} '@rolldown/pluginutils@1.0.0-beta.40': {} @@ -10484,6 +10607,10 @@ snapshots: dependencies: undici-types: 6.21.0 + '@types/node@24.10.13': + dependencies: + undici-types: 7.16.0 + '@types/node@25.0.3': dependencies: undici-types: 7.16.0 @@ -15842,6 +15969,10 @@ snapshots: dependencies: typescript: 5.9.3 + ts-blank-space@0.6.1: + dependencies: + typescript: 5.8.3 + tsconfck@3.1.6(typescript@5.9.3): optionalDependencies: typescript: 5.9.3 @@ -15929,6 +16060,8 @@ snapshots: transitivePeerDependencies: - supports-color + typescript@5.8.3: {} + typescript@5.9.3: {} ufo@1.6.1: {}