Skip to content
Merged
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
59 changes: 59 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Deploy

on:
push:
branches: [main]
# Build PRs too, so dependency updates are checked before they reach main.
pull_request:
workflow_dispatch:

permissions:
contents: read

jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read
pages: read
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v7
with:
node-version-file: .tool-versions
cache: npm
- run: npm ci
- run: npx tsc --noEmit
- run: npm test
- run: npm run build
# Links and canonical URLs end with a slash (/making/), but Void writes
# making.html, and GitHub Pages answers /making/ with a 404 unless
# making/index.html exists.
- run: |
find dist/client -name '*.html' ! -name index.html ! -name 404.html | while read -r file; do
mkdir -p "${file%.html}"
mv "$file" "${file%.html}/index.html"
done
- if: github.event_name != 'pull_request'
uses: actions/configure-pages@v6
- if: github.event_name != 'pull_request'
uses: actions/upload-pages-artifact@v5
with:
path: dist/client

deploy:
if: github.event_name != 'pull_request'
needs: build
runs-on: ubuntu-latest
permissions:
pages: write
id-token: write
concurrency:
group: pages
cancel-in-progress: false
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
steps:
- id: deployment
uses: actions/deploy-pages@v5
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/notes/
node_modules
dist
.void
.wrangler
.void-wrangler.jsonc
.env
.env.*
!.env.example
*.tsbuildinfo
.DS_Store
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 26.12.1
7 changes: 7 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<!--injected-by-void-v1.3.0-->
## Void

- This project uses [Void](https://void.cloud), a full-stack Vite framework for Cloudflare Workers with file-based API routing, server-rendered pages, and typed backend services.
- Use Void's CLI and typed APIs. Before working with Void, read the relevant Markdown docs in `node_modules/void/docs/`; start with `guide/quickstart.md` for setup and `reference/cli.md` for commands.

<!--/injected-by-void-->
1 change: 0 additions & 1 deletion CNAME

This file was deleted.

93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# makewith

[makewith.jp](https://makewith.jp) のソースです。
つくる人が集まり、つくり続ける場所として、もくもく会の案内や、つくったもの、つくっている途中の記録を載せています。

サイトは [Void](https://void.cloud) と React で作り、すべてのページをビルド時に HTML として書き出しています。
公開先は GitHub Pages です。

## 手元で動かす

Node のバージョンは `.tool-versions` に書いてあります。
[mise](https://mise.jdx.dev) か [asdf](https://asdf-vm.com) を使っている場合は、次のどちらかのコマンドで同じバージョンが入ります。

```sh
mise install # mise の場合
asdf install # asdf の場合
```

依存を入れて、開発サーバーを起動します。

```sh
npm ci
npm run dev
```

http://localhost:5173 で確認できます。

`void.config.ts` を変えたときや、記録を足したり frontmatter を書き換えたりしたときは、開発サーバーを再起動してください。
トップページと一覧ページに反映されないことがあります。

公開されるものと同じ形で確かめるときは、次のコマンドを使います。
型チェック、テスト、ビルドの順で、CI と同じことをします。
書き出したファイルは `dist/client/` に入ります。

```sh
npx tsc --noEmit
npm test
npm run build
```

## 記録を書く

記録は `pages/making/` に、1 件 1 ファイルの Markdown で書きます。
ファイル名がそのまま URL になります(`pages/making/first-meetup.md` なら `/making/first-meetup/`)。

```markdown
---
title: 第 1 回もくもく会
date: 2026-11-14
layout: article
author: 表示する名前
authorUrl: https://example.com
description: 一覧とトップページに出る 1〜2 文の説明
tags:
- もくもく会
image: /images/first-meetup.jpg
---

本文。タイトル、日付、名前はレイアウトが出すので、本文には書きません。
```

書くときの決まりは次のとおりです。

- `title`、`date`(`2026-11-14` の形)、`layout: article`、`author` は必ず書きます。書き忘れるとビルドが止まります。
- `author` は、記事に表示される名前です。本名でなくても構いません。`authorUrl` は任意で、書くと名前がそのページへのリンクになります。GitHub やブログ、個人サイトなど、自分を一番よく表すページを選んでください(`http://` か `https://` で始まる URL だけ)。
- `image` は任意です。画像は `public/images/` に置き、`/images/first-meetup.jpg` のように `/images/` から書きます。それ以外の形だとビルドが止まります。外部の画像も、保存してここに置きます。ページから他のサイトへ通信しないようにするためです。
- 日本語の段落は 1 行で書きます。段落の途中で改行すると、文と文の間に空白が入ります。
- 本文で `makewith.jp` と書くときは `makewith\.jp` とします。そのままだと自動でリンクになります。
- 下書きのファイル名は `*.dev.md` にします。開発サーバーでだけ表示され、公開用のビルドには含まれません。ただし、コミットするとリポジトリ上では誰でも読めるので、公開したくない下書きはコミットしないでください。

参加した人のものづくりや活動を載せるのは、本人が希望したときだけです。
PR で記録を送ってもらう場合は、本文の HTML に問題がないかを確かめてからマージします。

## ファイルの置き場所

| 場所 | 中身 |
|---|---|
| `pages/` | ページ。`index.island.tsx` がトップ、`(prose)/about.md` が About |
| `pages/making/` | 一覧ページと記録 |
| `pages/_layouts/article.island.tsx` | 記録のページのレイアウト |
| `pages/style.css` | サイト全体の CSS |
| `src/` | 部品(`components/`)、記録の集め方(`records.ts`)、記録の frontmatter の読み方と確認(`record-frontmatter.ts` とそのテスト)、OGP のタグの作り方(`og.ts` とそのテスト)、サイトの設定(`site.ts`) |
| `middleware/01.og.ts` | 各ページに OGP と canonical のタグを足す処理(中身は `src/og.ts`) |
| `public/` | そのまま公開されるファイル(画像、favicon) |

`pages/` や `middleware/` から `src/` を読み込むときは、`../src/` ではなく `#src/components/Photo` のように `#src/` から書きます(`package.json` の `imports` で設定しています)。

## 公開

`main` に入ると、GitHub Actions がビルドして GitHub Pages に公開します。
PR では、型チェック、テスト、ビルドだけを行います。

依存の更新は Renovate が PR で届けます。
51 changes: 0 additions & 51 deletions index.html

This file was deleted.

16 changes: 16 additions & 0 deletions middleware/01.og.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { defineMiddleware } from "void";
import { addOgTags } from "#src/og";
import { records } from "#src/records";
import { placeImage, placeImagePaths, siteName, siteUrl, titleSuffix } from "#src/site";

export default defineMiddleware(async (c, next) => {
await next();
if (!c.res.headers.get("content-type")?.includes("text/html")) return;

const path = c.req.path.endsWith("/") ? c.req.path : `${c.req.path}/`;
const record = records.find((r) => r.path === path);
const image = record?.image ?? (placeImagePaths.includes(path) ? placeImage : undefined);
const html = addOgTags(await c.res.text(), { path, isArticle: Boolean(record), image }, { name: siteName, url: siteUrl, titleSuffix });

c.res = new Response(html, c.res);
});
Loading
Loading