chore: scaffold twitter lite
This commit is contained in:
@@ -1,2 +1,4 @@
|
||||
.superpowers/
|
||||
.worktrees/
|
||||
.output/
|
||||
node_modules/
|
||||
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"$schema": "https://biomejs.dev/schemas/2.5.3/schema.json",
|
||||
"files": {
|
||||
"includes": ["**", "!src/routeTree.gen.ts", "!!**/.output"]
|
||||
},
|
||||
"formatter": { "enabled": true, "indentStyle": "space" },
|
||||
"linter": { "enabled": true, "rules": { "recommended": true } },
|
||||
"javascript": {
|
||||
"formatter": { "quoteStyle": "single", "semicolons": "asNeeded" }
|
||||
}
|
||||
}
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1783776592,
|
||||
"narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
description = "Intentional X reader";
|
||||
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
|
||||
outputs = { nixpkgs, ... }:
|
||||
let
|
||||
systems = [ "x86_64-linux" "aarch64-linux" ];
|
||||
forAllSystems = nixpkgs.lib.genAttrs systems;
|
||||
in {
|
||||
devShells = forAllSystems (system:
|
||||
let pkgs = import nixpkgs { inherit system; };
|
||||
in {
|
||||
default = pkgs.mkShell {
|
||||
packages = with pkgs; [ nodejs_22 pnpm chromium ];
|
||||
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD = "1";
|
||||
PLAYWRIGHT_CHROMIUM_EXECUTABLE =
|
||||
"${pkgs.chromium}/bin/chromium";
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "twitter-lite",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"packageManager": "[email protected]",
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
},
|
||||
"imports": {
|
||||
"#/*": "./src/*"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite dev --host 127.0.0.1 --port 3000",
|
||||
"dev:tailscale": "vite dev --host 0.0.0.0 --port 3000",
|
||||
"generate-routes": "tsr generate",
|
||||
"build": "vite build",
|
||||
"start": "HOST=127.0.0.1 node .output/server/index.mjs",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"lint": "biome check .",
|
||||
"format": "biome check --write .",
|
||||
"test": "vitest run",
|
||||
"test:watch": "vitest",
|
||||
"test:e2e": "playwright test",
|
||||
"test:live": "TWITTER_LITE_LIVE=1 vitest run tests/live/relay.test.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@tanstack/react-query": "5.101.2",
|
||||
"@tanstack/react-router": "1.170.17",
|
||||
"@tanstack/react-router-ssr-query": "1.167.1",
|
||||
"@tanstack/react-start": "1.168.27",
|
||||
"@yuta/bird": "link:../bird",
|
||||
"nitro": "3.0.260610-beta",
|
||||
"react": "19.2.7",
|
||||
"react-dom": "19.2.7",
|
||||
"zod": "4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "2.5.3",
|
||||
"@playwright/test": "1.61.1",
|
||||
"@tanstack/router-cli": "1.167.18",
|
||||
"@testing-library/dom": "10.4.1",
|
||||
"@testing-library/jest-dom": "6.9.1",
|
||||
"@testing-library/react": "16.3.2",
|
||||
"@types/node": "26.1.1",
|
||||
"@types/react": "19.2.17",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@vitejs/plugin-react": "6.0.3",
|
||||
"jsdom": "29.1.1",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "8.1.4",
|
||||
"vitest": "4.1.10"
|
||||
}
|
||||
}
|
||||
Generated
+3106
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
allowBuilds:
|
||||
esbuild: true
|
||||
@@ -0,0 +1,68 @@
|
||||
/* eslint-disable */
|
||||
|
||||
// @ts-nocheck
|
||||
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
|
||||
// This file was automatically generated by TanStack Router.
|
||||
// You should NOT make any changes in this file as it will be overwritten.
|
||||
// Additionally, you should also exclude this file from your linter and/or formatter to prevent it from being checked or modified.
|
||||
|
||||
import { Route as rootRouteImport } from './routes/__root'
|
||||
import { Route as IndexRouteImport } from './routes/index'
|
||||
|
||||
const IndexRoute = IndexRouteImport.update({
|
||||
id: '/',
|
||||
path: '/',
|
||||
getParentRoute: () => rootRouteImport,
|
||||
} as any)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRouteImport
|
||||
'/': typeof IndexRoute
|
||||
}
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths: '/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to: '/'
|
||||
id: '__root__' | '/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface FileRoutesByPath {
|
||||
'/': {
|
||||
id: '/'
|
||||
path: '/'
|
||||
fullPath: '/'
|
||||
preLoaderRoute: typeof IndexRouteImport
|
||||
parentRoute: typeof rootRouteImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
}
|
||||
export const routeTree = rootRouteImport
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
|
||||
import type { getRouter } from './router.tsx'
|
||||
import type { createStart } from '@tanstack/react-start'
|
||||
declare module '@tanstack/react-start' {
|
||||
interface Register {
|
||||
ssr: true
|
||||
router: Awaited<ReturnType<typeof getRouter>>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { QueryClient } from '@tanstack/react-query'
|
||||
import { createRouter } from '@tanstack/react-router'
|
||||
import { setupRouterSsrQueryIntegration } from '@tanstack/react-router-ssr-query'
|
||||
import { routeTree } from './routeTree.gen'
|
||||
|
||||
export function getRouter() {
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: { retry: false, staleTime: 0, gcTime: 0 },
|
||||
},
|
||||
})
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
context: { queryClient },
|
||||
scrollRestoration: true,
|
||||
defaultPreload: false,
|
||||
})
|
||||
|
||||
setupRouterSsrQueryIntegration({ router, queryClient })
|
||||
return router
|
||||
}
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: ReturnType<typeof getRouter>
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { QueryClient } from '@tanstack/react-query'
|
||||
import {
|
||||
createRootRouteWithContext,
|
||||
HeadContent,
|
||||
Scripts,
|
||||
} from '@tanstack/react-router'
|
||||
import appCss from '../styles.css?url'
|
||||
|
||||
type RouterContext = { queryClient: QueryClient }
|
||||
|
||||
export const Route = createRootRouteWithContext<RouterContext>()({
|
||||
head: () => ({
|
||||
meta: [
|
||||
{ charSet: 'utf-8' },
|
||||
{
|
||||
name: 'viewport',
|
||||
content: 'width=device-width, initial-scale=1',
|
||||
},
|
||||
{ title: 'Twitter Lite' },
|
||||
],
|
||||
links: [{ rel: 'stylesheet', href: appCss }],
|
||||
}),
|
||||
shellComponent: RootDocument,
|
||||
})
|
||||
|
||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="ja">
|
||||
<head>
|
||||
<HeadContent />
|
||||
</head>
|
||||
<body>
|
||||
{children}
|
||||
<Scripts />
|
||||
</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { Home } from './index'
|
||||
|
||||
describe('Home', () => {
|
||||
it('starts without ambient post content', () => {
|
||||
render(<Home />)
|
||||
|
||||
expect(screen.getByText('目的を決めてから開く')).toBeInTheDocument()
|
||||
expect(screen.queryByRole('article')).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,13 @@
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/')({ component: Home })
|
||||
|
||||
export function Home() {
|
||||
return (
|
||||
<main className="landing">
|
||||
<p className="brand">TWITTER LITE</p>
|
||||
<h1>目的を決めてから開く</h1>
|
||||
<p>ユーザー名か検索語を入力するまで、投稿は表示しません。</p>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
:root {
|
||||
color: #1e3238;
|
||||
background: #dfe7e9;
|
||||
font-family: Inter, "Noto Sans JP", ui-sans-serif, system-ui, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
min-width: 320px;
|
||||
min-height: 100vh;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
font: inherit;
|
||||
}
|
||||
|
||||
.landing {
|
||||
width: min(42rem, calc(100% - 2rem));
|
||||
margin: 15vh auto;
|
||||
padding: 2rem;
|
||||
border: 1px solid #c1cfd2;
|
||||
border-radius: 1rem;
|
||||
background: #f8faf9;
|
||||
}
|
||||
|
||||
.brand {
|
||||
color: #70858a;
|
||||
font-size: 0.7rem;
|
||||
letter-spacing: 0.2em;
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
import '@testing-library/jest-dom/vitest'
|
||||
import { cleanup } from '@testing-library/react'
|
||||
import { afterEach } from 'vitest'
|
||||
|
||||
afterEach(cleanup)
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"include": ["src/**/*.ts", "src/**/*.tsx", "tests/**/*.ts"],
|
||||
"compilerOptions": {
|
||||
"target": "ES2022",
|
||||
"jsx": "react-jsx",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Bundler",
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"types": ["vite/client", "node"],
|
||||
"paths": { "#/*": ["./src/*"] },
|
||||
"allowImportingTsExtensions": true,
|
||||
"noEmit": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
import { tanstackStart } from '@tanstack/react-start/plugin/vite'
|
||||
import viteReact from '@vitejs/plugin-react'
|
||||
import { nitro } from 'nitro/vite'
|
||||
import { defineConfig } from 'vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [nitro(), tanstackStart(), viteReact()],
|
||||
})
|
||||
@@ -0,0 +1,11 @@
|
||||
import path from 'node:path'
|
||||
import { configDefaults, defineConfig } from 'vitest/config'
|
||||
|
||||
export default defineConfig({
|
||||
resolve: { alias: { '#': path.resolve(import.meta.dirname, 'src') } },
|
||||
test: {
|
||||
environment: 'jsdom',
|
||||
setupFiles: ['./tests/setup.ts'],
|
||||
exclude: [...configDefaults.exclude, 'tests/e2e/**'],
|
||||
},
|
||||
})
|
||||
Reference in New Issue
Block a user