Closed16

Svelteで自分のサイトを開発する。

ymgnymgn

情報を集める際に、次のように分けて集めると良いかも知れない(SvelteKitとD1、DrizzleORMの組み合わせをピンポイントで扱っている記事があまり見当たらないので)。

  • SvelteKitとD1
  • SvelteKitとDrizzleORM
  • Cloudflare D1とDrizzleORM

参考リソース

公式のためになるリンク

https://github.com/sveltejs/kit/discussions/8463

ymgnymgn

記事投稿とコメントのテーブルを考えてみた。
DrizzleORMでは現段階ではCHECK制約はサポートされていないようなので、フロント側で弾くことになりそう。

自分で考えたSQL
-- 投稿した記事の管理
create table posts (
    id integer primary key,
    title text not null check (length(title) > 0),
    content text not null check (length(content) > 0),
    created_at text not null default current_timestamp,
    updated_at text not null default current_timestamp,
    deleted_at text not null default (datetime('1990-12-31 23:59:59')),
    is_published boolean default true
);

-- 記事に対するコメントの管理
create table comments (
    id integer primary key,
    post_id integer not null,
    author_name text not null check (length(author_name) > 0),
    content text not null check (length(content) > 0),
    created_at text not null default current_timestamp,
    updated_at text not null default current_timestamp,
    deleted_at text not null default (datetime('1990-12-31 23:59:59')),

    foreign key (post_id) references posts (id) on delete cascade
);
DrizzleORMでの実装
import { relations, sql } from 'drizzle-orm';
import { integer, sqliteTable, text } from 'drizzle-orm/sqlite-core';

export const posts = sqliteTable('posts', {
  id: integer('id').primaryKey(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  createdAt: text('created_at')
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
  updatedAt: text('updated_at')
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
  deletedAt: text('deleted_at')
    .notNull()
    .default(sql`(datetime('1990-12-31 23:59:59'))`),
  isPublished: integer('is_published', { mode: 'boolean' }).default(true),
});

export const postsRelations = relations(posts, ({ many }) => ({
  comments: many(comments),
}));

export const comments = sqliteTable('comments', {
  id: integer('id').primaryKey(),
  postId: integer('post_id')
    .notNull()
    .references(() => posts.id, { onDelete: 'cascade' }),
  authorName: text('author_name').notNull(),
  content: text('content').notNull(),
  createdAt: text('created_at')
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
  updatedAt: text('updated_at')
    .notNull()
    .default(sql`CURRENT_TIMESTAMP`),
  deletedAt: text('deleted_at')
    .notNull()
    .default(sql`(datetime('1990-12-31 23:59:59'))`),
});

export const commentsRelations = relations(comments, ({ one }) => ({
  post: one(posts, {
    fields: [comments.postId],
    references: [posts.id],
  }),
}));
DrizzleORMによって生成されたSQL
CREATE TABLE `comments` (
	`id` integer PRIMARY KEY NOT NULL,
	`post_id` integer NOT NULL,
	`author_name` text NOT NULL,
	`content` text NOT NULL,
	`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
	`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
	`deleted_at` text DEFAULT (datetime('1990-12-31 23:59:59')) NOT NULL,
	FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `posts` (
	`id` integer PRIMARY KEY NOT NULL,
	`title` text NOT NULL,
	`content` text NOT NULL,
	`created_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
	`updated_at` text DEFAULT CURRENT_TIMESTAMP NOT NULL,
	`deleted_at` text DEFAULT (datetime('1990-12-31 23:59:59')) NOT NULL,
	`is_published` integer DEFAULT true
);
ymgnymgn

トラブルシューティング

SvelteKitとD1の開発環境起動

https://kit.svelte.jp/docs/adapter-cloudflare-workers

platform.env は本番向けビルドでのみ利用することができます。ローカルでテストするには wrangler を使ってください

起動方法がまだ分かっていない。
wrangler d1 execute <DATABASE_NAME> --file xxx/xxx.sql --localを実行し、ローカル環境のDBにSQL実行した後に、wrangler devでローカル環境立ち上げでできた。

ymgnymgn

各種設定パッケージ・設定ファイルの詳細がよく分からないので調べていきたい。

パッケージ
キャッチアップ済み パッケージ名 キャッチアップ内容
x typescript-eslint/eslint-plugin ESLintがTypeScriptをサポートできるようにするためのパッケージ。TypeScript用のLintが用意されている。
x typescript-eslint/parser 上記のTypeScript用パーサ
x eslint ESLint
x eslint-config-prettier Prettier公式パッケージ。Prettierと競合するESLintルールを無効化する。
x eslint-plugin-svelte ESLintがSvelteをサポートできるようにするためのパッケージ。Svelteのパーサも含まれている?
x prettier Prettier
x prettier-plugin-svelte PrettierがSvelteをサポートできるようにするためのパッケージ。
x vite ヴィート。ビルドツール。詳細はまだよく分かっていない。
設定ファイル
キャッチアップ済み ファイル名 キャッチアップ内容
x .eslintignore ESLintのリント対象外にするファイル、ディレクトリを指定。
x .eslintrc.cjs ESLintの設定ファイル。
x .gitignore Gitの管理対象外にするファイル、ディレクトリを指定。
x .npmrc npmの設定ファイル。コンソール表示を無効にできたり挙動を変えられるそうだが詳細はよく分かっていない。
x .prettierignore Prettierの対象外にするファイル、ディレクトリを指定。
x .prettierrc Prettierの設定ファイル。
x svelte.config.js SvelteKitの設定ファイル。
x tsconfig.json TypeScriptのコンパイラオプションなどの挙動を設定。
x vite.config.ts Viteの設定ファイル。いまいちよく分かっていない。
ymgnymgn

ダークモードの対応方法について

この方法が良さそう?
cookieの有効期限はこれでいいのかという気がするが。
https://www.davidwparker.com/posts/dark-mode-in-sveltekit-with-and-without-javascript
→SvelteKitのバージョンが古いのか、記述方法が変わっている部分が多く解読が困難なため断念。

代わりにこのコードを参考にするのが良さそう。
https://github.com/jmagrippis/moving-scapes

別スクラップで対応できたのでメモ。
https://zenn.dev/ymgn_dev/scraps/0319608a717701

ymgnymgn

投稿詳細ページのMarkdownのスタイル候補。

導入済み

要検討

  • https://rehype-pretty-code.netlify.app/
    • シンタックスハイライトはこれでいい気がする
      • この問題にぶち当たったので、結局rehype-highlightでハイライトするように戻した。ライト、ダークモード両方でも見やすさが変わらない(コード部分の背景色がページの背景色と同化しにくい)tokyo-night-dark.cssを当てた。
ymgnymgn

スタイル調整をだいぶ頑張って見た目がいい感じになった。

Typography - shadcn-svelteのタイポグラフィーに少し自分好みのアレンジを加えた見た目にしたかったので、tailwindcss/typographyは使わなかった。

ymgnymgn

SvelteKitにフォントを導入する手順はこの記事が詳しい。

https://dev.to/khromov/adding-locally-hosted-google-fonts-to-your-sveltekit-project-26kd

FontsourceがGoogle Fontsも含めてnpmパッケージとして提供しているそうで強い。
https://fontsource.org/

この記事はSvelteKit + Tailwind CSS でのフォント変更について解説していて分かりやすい。
https://www.jvp.design/blog/self-hosting-a-font-with-tailwind-and-sveltekit

Google Fontsのおすすめフォントがまとまってて分かりやすい。
https://wkwkdesign.com/japanese-googlefonts/

個人的にはこのフォントが好きで使いたいので、ライセンスなどを確認しておく。
https://github.com/yuru7/HackGen

対応できたのでここにまとめた。
https://zenn.dev/ymgn_dev/scraps/c1ca94db0a4bb8

ymgnymgn

最初に書く記事の案

  • SvelteKitでSSRに対応したダークモード対応を行う方法
  • SvelteKit + Tailwind CSSのフォント差し替え
ymgnymgn

管理画面の実装に着手した。
なるべくサイトと同じようにシンプルに作りたいところ。

記事の管理画面にはshadcn-svelteのData Tableを使うのがちょうど良さそう。

投稿管理画面を仮でおいた。
管理画面ではヘッダー右側のPostsをクリックすると、投稿一覧画面へ遷移するようにする予定。
サイトと同じ見た目のヘッダーを使いつつ、サイト側ではPostsをクリックすると閲覧用の投稿一覧画面が表示され、管理画面側では投稿の管理画面が表示されるようにしている。

ymgnymgn

管理画面の実装進捗。
結構いい感じのシンプルな見た目になったんではないか。


ymgnymgn

OGPの実装をした。動的に画像を生成するのは面倒そう(satoriで実装する方法)だったので、一旦静的な画像ファイルを返しているだけ。

https://ymgn.dev

ymgnymgn

この後、色々あってT3 Stack(Next.js) + Vercel(サイトデプロイ、Vercel Postgres, Vercel Blob)を使っていくことにした。
只今絶賛書き換え中。

  • Next.js(App Router)
  • markdown-it
このスクラップは2023/11/13にクローズされました