init
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
generated/
|
||||
41
README.md
Normal file
41
README.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# 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)
|
||||
135
bun.lock
Normal file
135
bun.lock
Normal file
@@ -0,0 +1,135 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "my-website",
|
||||
"dependencies": {
|
||||
"@mastrojs/markdown": "npm:@jsr/mastrojs__markdown@^0",
|
||||
"@mastrojs/mastro": "npm:@jsr/mastrojs__mastro@^0",
|
||||
"@remix-run/node-fetch-server": "^0.11",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24",
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@jsr/mastrojs__mastro": ["@jsr/mastrojs__mastro@0.6.4", "https://npm.jsr.io/~/11/@jsr/mastrojs__mastro/0.6.4.tgz", { "dependencies": { "@jsr/std__http": "^1.0.19", "@jsr/std__media-types": "^1.1.0", "@jsr/std__path": "1.1.4", "ts-blank-space": "0.6.1" } }, "sha512-w1YnGd++Ev3xq/E6Lzy8mtNKCMBoe2dBxpv4AjNJsdAr2XqFlX0z74Tno3UmuYZ7Ijzmr4YnOK/iEAGe1L3NEg=="],
|
||||
|
||||
"@jsr/std__bytes": ["@jsr/std__bytes@1.0.6", "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.6.tgz", {}, "sha512-St6yKggjFGhxS52IFLJWvkchRFbAKg2Xh8UxA4S1EGz7GJ2Ui+ssDDldj/w2c8vCxvl6qgR0HaYbKeFJNqujmA=="],
|
||||
|
||||
"@jsr/std__cli": ["@jsr/std__cli@1.0.27", "https://npm.jsr.io/~/11/@jsr/std__cli/1.0.27.tgz", { "dependencies": { "@jsr/std__fmt": "^1.0.9", "@jsr/std__internal": "^1.0.12" } }, "sha512-aaY6VYkdv0qmAIiaYNfBH9Pd3Te5bJsEUQmNg/ak43AorET5+pBcI9RgqCgBXfkb2tBnLUlTBklQvirMz/CnAQ=="],
|
||||
|
||||
"@jsr/std__encoding": ["@jsr/std__encoding@1.0.10", "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.10.tgz", {}, "sha512-WK2njnDTyKefroRNk2Ooq7GStp6Y0ccAvr4To+Z/zecRAGe7+OSvH9DbiaHpAKwEi2KQbmpWMOYsdNt+TsdmSw=="],
|
||||
|
||||
"@jsr/std__fmt": ["@jsr/std__fmt@1.0.9", "https://npm.jsr.io/~/11/@jsr/std__fmt/1.0.9.tgz", {}, "sha512-YFJJMozmORj2K91c5J9opWeh0VUwrd+Mwb7Pr0FkVCAKVLu2UhT4LyvJqWiyUT+eF+MdfqQ9F7RtQj4bXn9Smw=="],
|
||||
|
||||
"@jsr/std__fs": ["@jsr/std__fs@1.0.22", "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.22.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12", "@jsr/std__path": "^1.1.4" } }, "sha512-PvDtgT25IqhFEX2LjQI0aTz/Wg61jCtJ8l19fE9MUSvSmtw57Kzr6sM7GcCsSrsZEdQ7wjLfXvvhy8irta4Zww=="],
|
||||
|
||||
"@jsr/std__html": ["@jsr/std__html@1.0.5", "https://npm.jsr.io/~/11/@jsr/std__html/1.0.5.tgz", {}, "sha512-8ypLaw6ORY7jisEvsXOS/D631/pMCX78mV7fyromfzJXxqb35OUNCBC2E4Ca0goKQJW8I2XhEgoFu0ZXaIiGvA=="],
|
||||
|
||||
"@jsr/std__http": ["@jsr/std__http@1.0.24", "https://npm.jsr.io/~/11/@jsr/std__http/1.0.24.tgz", { "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" } }, "sha512-mfUI8vAkMVvf0wYxkZd9ZKfwFryLanHe+nbvxfFPkNO24B2IY6knkBJNN28cTZ8SITh8t8rv56Cx5uOAc0uGFg=="],
|
||||
|
||||
"@jsr/std__internal": ["@jsr/std__internal@1.0.12", "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.12.tgz", {}, "sha512-6xReMW9p+paJgqoFRpOE2nogJFvzPfaLHLIlyADYjKMUcwDyjKZxryIbgcU+gxiTygn8yCjld1HoI0ET4/iZeA=="],
|
||||
|
||||
"@jsr/std__media-types": ["@jsr/std__media-types@1.1.0", "https://npm.jsr.io/~/11/@jsr/std__media-types/1.1.0.tgz", {}, "sha512-dHvaxHL7ENWnltgL653uo3KnKFse3ZbopZop2gqsT7yrscx7irZEClu5Cba7gMPPRk4Lg1FbriNcaBViM2RSBw=="],
|
||||
|
||||
"@jsr/std__net": ["@jsr/std__net@1.0.6", "https://npm.jsr.io/~/11/@jsr/std__net/1.0.6.tgz", {}, "sha512-mh27Fw4UMCjGSIMoOhjia5cS5fNP9M9DZYhGB7EYSZNnzf/eguFiarii/W4oDwYMmnxCMouUzhc6Y7jFuwTzcg=="],
|
||||
|
||||
"@jsr/std__path": ["@jsr/std__path@1.1.4", "https://npm.jsr.io/~/11/@jsr/std__path/1.1.4.tgz", { "dependencies": { "@jsr/std__internal": "^1.0.12" } }, "sha512-SK4u9H6NVTfolhPdlvdYXfNFefy1W04AEHWJydryYbk+xqzNiVmr5o7TLJLJFqwHXuwMRhwrn+mcYeUfS0YFaA=="],
|
||||
|
||||
"@jsr/std__streams": ["@jsr/std__streams@1.0.17", "https://npm.jsr.io/~/11/@jsr/std__streams/1.0.17.tgz", { "dependencies": { "@jsr/std__bytes": "^1.0.6" } }, "sha512-LnPlWk20mDIV5/nqoUomAB8umOimfGEyWRApxLgekXFuqKGDsGpUAi58amieVU2XAGNclmUOtQOcQ/qOl3PNFg=="],
|
||||
|
||||
"@mastrojs/markdown": ["@jsr/mastrojs__markdown@0.1.4", "https://npm.jsr.io/~/11/@jsr/mastrojs__markdown/0.1.4.tgz", { "dependencies": { "@jsr/mastrojs__mastro": "0", "js-yaml": "^4.1.0", "micromark": "^4.0.2", "micromark-extension-gfm": "^3.0.0" } }, "sha512-wewX6u/Rw19azjAl6Km0tO97IuAllTA+Y9OwYZdG6qShtkTTsx5Kx9Cr2Qo99+1bUd58ElU/qKgKgrzqYPzO+A=="],
|
||||
|
||||
"@mastrojs/mastro": ["@jsr/mastrojs__mastro@0.6.4", "https://npm.jsr.io/~/11/@jsr/mastrojs__mastro/0.6.4.tgz", { "dependencies": { "@jsr/std__http": "^1.0.19", "@jsr/std__media-types": "^1.1.0", "@jsr/std__path": "1.1.4", "ts-blank-space": "0.6.1" } }, "sha512-w1YnGd++Ev3xq/E6Lzy8mtNKCMBoe2dBxpv4AjNJsdAr2XqFlX0z74Tno3UmuYZ7Ijzmr4YnOK/iEAGe1L3NEg=="],
|
||||
|
||||
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.11.0", "", {}, "sha512-nCrFHVxDFioSHc0g/3m5ztwgjBt7g8qh/UwmYkDjuMePKFepMKfNGgH5S6L7iXKX+jUrf3ooVmhx3NGIoa9iYA=="],
|
||||
|
||||
"@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="],
|
||||
|
||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||
|
||||
"@types/node": ["@types/node@24.10.13", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decode-named-character-reference": ["decode-named-character-reference@1.3.0", "", { "dependencies": { "character-entities": "^2.0.0" } }, "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q=="],
|
||||
|
||||
"dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="],
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
|
||||
|
||||
"micromark": ["micromark@4.0.2", "", { "dependencies": { "@types/debug": "^4.0.0", "debug": "^4.0.0", "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-decode-numeric-character-reference": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA=="],
|
||||
|
||||
"micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="],
|
||||
|
||||
"micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="],
|
||||
|
||||
"micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="],
|
||||
|
||||
"micromark-extension-gfm-footnote": ["micromark-extension-gfm-footnote@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-core-commonmark": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw=="],
|
||||
|
||||
"micromark-extension-gfm-strikethrough": ["micromark-extension-gfm-strikethrough@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw=="],
|
||||
|
||||
"micromark-extension-gfm-table": ["micromark-extension-gfm-table@2.1.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg=="],
|
||||
|
||||
"micromark-extension-gfm-tagfilter": ["micromark-extension-gfm-tagfilter@2.0.0", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg=="],
|
||||
|
||||
"micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="],
|
||||
|
||||
"micromark-factory-destination": ["micromark-factory-destination@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA=="],
|
||||
|
||||
"micromark-factory-label": ["micromark-factory-label@2.0.1", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg=="],
|
||||
|
||||
"micromark-factory-space": ["micromark-factory-space@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg=="],
|
||||
|
||||
"micromark-factory-title": ["micromark-factory-title@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw=="],
|
||||
|
||||
"micromark-factory-whitespace": ["micromark-factory-whitespace@2.0.1", "", { "dependencies": { "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ=="],
|
||||
|
||||
"micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="],
|
||||
|
||||
"micromark-util-chunked": ["micromark-util-chunked@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA=="],
|
||||
|
||||
"micromark-util-classify-character": ["micromark-util-classify-character@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q=="],
|
||||
|
||||
"micromark-util-combine-extensions": ["micromark-util-combine-extensions@2.0.1", "", { "dependencies": { "micromark-util-chunked": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg=="],
|
||||
|
||||
"micromark-util-decode-numeric-character-reference": ["micromark-util-decode-numeric-character-reference@2.0.2", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw=="],
|
||||
|
||||
"micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="],
|
||||
|
||||
"micromark-util-html-tag-name": ["micromark-util-html-tag-name@2.0.1", "", {}, "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA=="],
|
||||
|
||||
"micromark-util-normalize-identifier": ["micromark-util-normalize-identifier@2.0.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0" } }, "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q=="],
|
||||
|
||||
"micromark-util-resolve-all": ["micromark-util-resolve-all@2.0.1", "", { "dependencies": { "micromark-util-types": "^2.0.0" } }, "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg=="],
|
||||
|
||||
"micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="],
|
||||
|
||||
"micromark-util-subtokenize": ["micromark-util-subtokenize@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA=="],
|
||||
|
||||
"micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="],
|
||||
|
||||
"micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"ts-blank-space": ["ts-blank-space@0.6.1", "", { "dependencies": { "typescript": "5.1.6 - 5.8.x" } }, "sha512-LcM3W5HEyzTaXUeQITV8ploUOGe+zuuoFYsCfPscFLhx3bZn2sSfHMKxsULVG/zA7an9UhReiHv4Kk/6QzlpXQ=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||
|
||||
"ts-blank-space/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
}
|
||||
}
|
||||
12
components/Footer.ts
Normal file
12
components/Footer.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { html } from "@mastrojs/mastro";
|
||||
|
||||
export const Footer = () =>
|
||||
html`
|
||||
<footer>
|
||||
<div>
|
||||
Check us out
|
||||
<a href="https://github.com/mastrojs/mastro">on GitHub</a>.
|
||||
© ${new Date().getFullYear()}
|
||||
</div>
|
||||
</footer>
|
||||
`;
|
||||
7
components/Header.ts
Normal file
7
components/Header.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { html } from "@mastrojs/mastro";
|
||||
|
||||
export const Header = () => html`
|
||||
<header>
|
||||
<div><a href="/">jens.pub</a></div>
|
||||
</header>
|
||||
`;
|
||||
31
components/Layout.ts
Normal file
31
components/Layout.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { ghPagesBasePath, html, type Html } from "@mastrojs/mastro";
|
||||
import { Header } from "./Header.ts";
|
||||
import { Footer } from "./Footer.ts";
|
||||
|
||||
export const basePath = ghPagesBasePath();
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
children: Html;
|
||||
}
|
||||
|
||||
export const Layout = (props: Props) =>
|
||||
html`
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>${props.title}</title>
|
||||
<link rel="stylesheet" href=${basePath + "/styles.css"}>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
${Header()}
|
||||
|
||||
<main>
|
||||
${props.children}
|
||||
</main>
|
||||
|
||||
${Footer()}
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
28
data/posts/2024-01-30-hello-world.md
Normal file
28
data/posts/2024-01-30-hello-world.md
Normal file
@@ -0,0 +1,28 @@
|
||||
---
|
||||
title: Hello World
|
||||
date: 2024-01-30
|
||||
---
|
||||
|
||||
Markdown is just a simpler syntax for the most commonly used HTML
|
||||
elements when writing body text.
|
||||
|
||||
A blank line, like above, marks a new paragraph (HTML `<p>`).
|
||||
A line starting with `##` is an HTML `<h2>`:
|
||||
|
||||
## Lists
|
||||
|
||||
An example of an unordered list:
|
||||
|
||||
- item one
|
||||
- item two
|
||||
|
||||
And an ordered list:
|
||||
|
||||
1. item one
|
||||
2. item two
|
||||
|
||||
|
||||
## More info
|
||||
|
||||
See [CommonMark](https://commonmark.org) for more
|
||||
information about Markdown.
|
||||
6
data/posts/2024-01-31-second-post.md
Normal file
6
data/posts/2024-01-31-second-post.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
title: Second Post
|
||||
date: 2024-01-31
|
||||
---
|
||||
|
||||
This is our second blog post.
|
||||
24
package.json
Normal file
24
package.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "my-website",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"start": "node --watch server.ts",
|
||||
"generate": "node node_modules/@mastrojs/mastro/src/generator.js",
|
||||
"check": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@mastrojs/mastro": "npm:@jsr/mastrojs__mastro@^0",
|
||||
"@remix-run/node-fetch-server": "^0.11",
|
||||
"@mastrojs/markdown": "npm:@jsr/mastrojs__markdown@^0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24",
|
||||
"typescript": "^5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=24.12"
|
||||
},
|
||||
"volta": {
|
||||
"node": "24.12.0"
|
||||
}
|
||||
}
|
||||
88
routes/index.server.ts
Normal file
88
routes/index.server.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import { html, htmlToResponse } from "@mastrojs/mastro";
|
||||
import { Layout } from "../components/Layout.ts";
|
||||
|
||||
export const GET = () =>
|
||||
htmlToResponse(
|
||||
Layout({
|
||||
title: "Home",
|
||||
children: html`
|
||||
<nav>
|
||||
<p>👉 <a href="/news/">News</a></p>
|
||||
</nav>
|
||||
|
||||
<h1>Common HTML elements</h1>
|
||||
<p>
|
||||
Let's go through the most important HTML elements to
|
||||
structure your content:
|
||||
</p>
|
||||
|
||||
<h2>Paragraphs</h2>
|
||||
<p>The p element marks a paragraph of text.</p>
|
||||
|
||||
<h2>Headings</h2>
|
||||
<p>
|
||||
At the very top of the body, we have the heading of this page
|
||||
in an h1 element. This is what search engines (like Google)
|
||||
and screen readers (used by visually impaired readers) look for
|
||||
when they want to know what the page's title is. Therefore, you
|
||||
should only ever have one h1 element on any given page.
|
||||
</p>
|
||||
<p>
|
||||
The h2 element is a sub-heading. HTML has h1 up to h6 elements,
|
||||
to mark progressively deeper nested sub-headings. You should
|
||||
use those to mark the structure of your page. All headings
|
||||
together should act like a table of contents for your page.
|
||||
</p>
|
||||
|
||||
<h2>Lists</h2>
|
||||
<p>
|
||||
Let's add an ordered list
|
||||
(meaning the list markers will be numbers):
|
||||
</p>
|
||||
<ol>
|
||||
<li>list item one</li>
|
||||
<li>list item two</li>
|
||||
<li>list item three</li>
|
||||
</ol>
|
||||
<p>
|
||||
and an unordered list
|
||||
(the list markers will be bullet points):
|
||||
</p>
|
||||
<ul>
|
||||
<li>list item one</li>
|
||||
<li>list item two</li>
|
||||
<li>list item three</li>
|
||||
</ul>
|
||||
|
||||
<h2>Formatting</h2>
|
||||
<p>
|
||||
Note how all elements introduced so far cause a line-break
|
||||
before and after them? That's because they are so-called
|
||||
<a href="https://developer.mozilla.org/en-US/docs/Glossary/Block-level_content">block elements</a>.
|
||||
</p>
|
||||
<p>
|
||||
However, links (like the a element we just saw),
|
||||
<em>emphasis</em> (rendered as italics), and
|
||||
<strong>strong emphasis</strong> (rendered bold),
|
||||
are all inline elements. That means they don't cause
|
||||
any line-breaks by default.
|
||||
</p>
|
||||
|
||||
<h2>An image</h2>
|
||||
<img src="chair.jpg" alt="A chair" height="300">
|
||||
<p>
|
||||
We will add an image file <code>chair.jpg</code> later.
|
||||
</p>
|
||||
<p>
|
||||
For now, note the alt attribute on the image. It is required
|
||||
and contains "alternative text" that is read to visually
|
||||
impaired readers, or shown if the image fails to load.
|
||||
</p>
|
||||
<p>
|
||||
If the image is relevant content, the alt text should
|
||||
therefore be a brief description of what's in the image.
|
||||
If the image is just decoration, you should use alt="".
|
||||
</p>
|
||||
`,
|
||||
}),
|
||||
);
|
||||
19
routes/news/[slug]/index.server.ts
Normal file
19
routes/news/[slug]/index.server.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import { getParams, htmlToResponse, readDir } from "@mastrojs/mastro";
|
||||
import { readMarkdownFile } from "@mastrojs/markdown";
|
||||
import { Layout } from "../../../components/Layout.ts";
|
||||
|
||||
export const GET = async (req: Request) => {
|
||||
const { slug } = getParams(req);
|
||||
const post = await readMarkdownFile(`data/posts/${slug}.md`);
|
||||
return htmlToResponse(
|
||||
Layout({
|
||||
title: post.meta.title,
|
||||
children: post.content,
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
export const getStaticPaths = async () => {
|
||||
const posts = await readDir("data/posts/");
|
||||
return posts.map((p) => "/news/" + p.slice(0, -3) + "/");
|
||||
};
|
||||
21
routes/news/index.server.ts
Normal file
21
routes/news/index.server.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { html, htmlToResponse } from "@mastrojs/mastro";
|
||||
import { readMarkdownFiles } from "@mastrojs/markdown";
|
||||
import { Layout } from "../../components/Layout.ts";
|
||||
|
||||
export const GET = async () => {
|
||||
const posts = await readMarkdownFiles("data/posts/*.md");
|
||||
return htmlToResponse(
|
||||
Layout({
|
||||
title: "News",
|
||||
children: posts.map((post) =>
|
||||
html`
|
||||
<p>
|
||||
<a href="${post.path.slice(12, -3) + "/"}">
|
||||
${post.meta.title}
|
||||
</a>
|
||||
</p>
|
||||
`
|
||||
),
|
||||
}),
|
||||
);
|
||||
};
|
||||
129
routes/styles.css
Normal file
129
routes/styles.css
Normal file
@@ -0,0 +1,129 @@
|
||||
/* 1. Use a more-intuitive box-sizing model */
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 2. Remove default margin */
|
||||
*:not(dialog) {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 3. Enable keyword animations */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
html {
|
||||
interpolate-size: allow-keywords;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
/* 4. Add accessible line-height */
|
||||
line-height: 1.5;
|
||||
/* 5. Improve text rendering */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* 6. Improve media defaults */
|
||||
img,
|
||||
picture,
|
||||
video,
|
||||
canvas,
|
||||
svg {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* 7. Inherit fonts for form controls */
|
||||
input,
|
||||
button,
|
||||
textarea,
|
||||
select {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
/* 8. Avoid text overflows */
|
||||
p,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
overflow-wrap: break-word;
|
||||
}
|
||||
|
||||
/* 9. Improve line wrapping */
|
||||
p {
|
||||
text-wrap: pretty;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
text-wrap: balance;
|
||||
}
|
||||
|
||||
/*
|
||||
10. Create a root stacking context
|
||||
*/
|
||||
#root,
|
||||
#__next {
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/*
|
||||
real css
|
||||
*/
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr auto;
|
||||
min-height: 100vh;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 18px;
|
||||
margin: 0 auto;
|
||||
--brand-color: rebeccapurple;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: var(--brand-color);
|
||||
color: whitesmoke;
|
||||
font-weight: bold;
|
||||
font-size: 50px;
|
||||
padding: 1.5em 1em 1em 1em;
|
||||
}
|
||||
|
||||
main {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: var(--brand-color);
|
||||
color: whitesmoke;
|
||||
padding: 2em 1em;
|
||||
margin-top: 3em;
|
||||
}
|
||||
|
||||
p {
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
header > div,
|
||||
main,
|
||||
footer > div {
|
||||
width: 100%;
|
||||
max-width: 30rem;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
header > a,
|
||||
a:visited {
|
||||
color: white;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration-line: none;
|
||||
}
|
||||
15
server.ts
Normal file
15
server.ts
Normal file
@@ -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}`);
|
||||
});
|
||||
15
tsconfig.json
Normal file
15
tsconfig.json
Normal file
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user