Update to next 13, switch to pnpm (#127)

* switch to pnpm

* dep improvements, style fixes, next/link codemod

* server: upgrade sqlite
This commit is contained in:
Max Leiter 2022-11-08 00:28:19 -08:00 committed by GitHub
parent 9771e64f93
commit 55c5ecfe6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
31 changed files with 9005 additions and 3401 deletions

4
client/.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,4 @@
{
"typescript.tsdk": "./node_modules/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true
}

View file

@ -1,17 +0,0 @@
import type { LinkProps } from "@geist-ui/core"
import { Link as GeistLink } from "@geist-ui/core"
import { useRouter } from "next/router"
const Link = (props: LinkProps) => {
const { basePath } = useRouter()
const propHrefWithoutLeadingSlash =
props.href && props.href.startsWith("/")
? props.href.substring(1)
: props.href
const href = basePath
? `${basePath}/${propHrefWithoutLeadingSlash}`
: props.href
return <GeistLink {...props} href={href} />
}
export default Link

View file

@ -1,4 +1,3 @@
import { Text, Spacer } from "@geist-ui/core"
import Cookies from "js-cookie" import Cookies from "js-cookie"
import styles from "./admin.module.css" import styles from "./admin.module.css"
import PostTable from "./post-table" import PostTable from "./post-table"
@ -23,11 +22,19 @@ export const adminFetcher = async (
const Admin = () => { const Admin = () => {
return ( return (
<div className={styles.adminWrapper}> <div className={styles.adminWrapper}>
<Text h2>Administration</Text> <h2>Administration</h2>
<div
style={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: 4
}}
>
<UserTable /> <UserTable />
<Spacer height={1} />
<PostTable /> <PostTable />
</div> </div>
</div>
) )
} }

View file

@ -1,10 +1,12 @@
import { FormEvent, useEffect, useState } from "react" import { FormEvent, useEffect, useState } from "react"
import { Button, Input, Text, Note } from "@geist-ui/core"
import styles from "./auth.module.css" import styles from "./auth.module.css"
import { useRouter } from "next/router" import { useRouter } from "next/router"
import Link from "../Link" import Link from "../link"
import Cookies from "js-cookie" import Cookies from "js-cookie"
import useSignedIn from "@lib/hooks/use-signed-in" import useSignedIn from "@lib/hooks/use-signed-in"
import Input from "@components/input"
import Button from "@components/button"
import Note from "@components/note"
const NO_EMPTY_SPACE_REGEX = /^\S*$/ const NO_EMPTY_SPACE_REGEX = /^\S*$/
const ERROR_MESSAGE = const ERROR_MESSAGE =
@ -90,58 +92,57 @@ const Auth = ({ page }: { page: "signup" | "signin" }) => {
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<div className={styles.formGroup}> <div className={styles.formGroup}>
<Input <Input
htmlType="text" type="text"
id="username" id="username"
value={username} value={username}
onChange={(event) => setUsername(event.target.value)} onChange={(event) => setUsername(event.currentTarget.value)}
placeholder="Username" placeholder="Username"
required required
scale={4 / 3}
/> />
<Input <Input
htmlType="password" type="password"
id="password" id="password"
value={password} value={password}
onChange={(event) => setPassword(event.target.value)} onChange={(event) => setPassword(event.currentTarget.value)}
placeholder="Password" placeholder="Password"
required required
scale={4 / 3}
/> />
{requiresServerPassword && ( {requiresServerPassword && (
<Input <Input
htmlType="password" type="password"
id="server-password" id="server-password"
value={serverPassword} value={serverPassword}
onChange={(event) => setServerPassword(event.target.value)} onChange={(event) =>
setServerPassword(event.currentTarget.value)
}
placeholder="Server Password" placeholder="Server Password"
required required
scale={4 / 3}
/> />
)} )}
<Button type="success" htmlType="submit"> <Button buttonType="primary" type="submit">
{signingIn ? "Sign In" : "Sign Up"} {signingIn ? "Sign In" : "Sign Up"}
</Button> </Button>
</div> </div>
<div className={styles.formContentSpace}> <div className={styles.formContentSpace}>
{signingIn ? ( {signingIn ? (
<Text> <p>
Don&apos;t have an account?{" "} Don&apos;t have an account?{" "}
<Link color href="/signup"> <Link colored href="/signup">
Sign up Sign up
</Link> </Link>
</Text> </p>
) : ( ) : (
<Text> <p>
Already have an account?{" "} Already have an account?{" "}
<Link color href="/signin"> <Link colored href="/signin">
Sign in Sign in
</Link> </Link>
</Text> </p>
)} )}
</div> </div>
{errorMsg && ( {errorMsg && (
<Note scale={0.75} type="error"> <Note type="error">
{errorMsg} {errorMsg}
</Note> </Note>
)} )}

View file

@ -1,26 +1,22 @@
.button:root {
--hover: var(--bg);
--hover-bg: var(--fg);
}
.button { .button {
user-select: none; user-select: none;
cursor: pointer; cursor: pointer;
border-radius: var(--radius); border-radius: var(--radius);
color: var(--input-fg); border: 1px solid var(--border);
font-weight: 400; padding: var(--gap-half) var(--gap);
font-size: 1.1rem;
background: var(--input-bg);
border: var(--input-border);
height: 2rem;
display: flex;
align-items: center;
padding: var(--gap-quarter) var(--gap-half);
transition: background-color var(--transition), color var(--transition);
width: 100%;
height: var(--input-height);
} }
.button:hover, .button:hover,
.button:focus { .button:focus {
outline: none; outline: none;
background: var(--input-bg-hover); color: var(--hover);
border: var(--input-border-focus); background: var(--hover-bg);
border: var(--);
} }
.button[disabled] { .button[disabled] {
@ -38,3 +34,20 @@
background: var(--fg); background: var(--fg);
color: var(--bg); color: var(--bg);
} }
.icon {
display: inline-block;
width: 1em;
height: 1em;
vertical-align: middle;
}
.iconRight {
margin-left: var(--gap-half);
}
.icon svg {
display: block;
width: 100%;
height: 100%;
}

View file

@ -6,6 +6,7 @@ type Props = React.HTMLProps<HTMLButtonElement> & {
buttonType?: "primary" | "secondary" buttonType?: "primary" | "secondary"
className?: string className?: string
onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void onClick?: (e: React.MouseEvent<HTMLButtonElement>) => void
iconRight?: React.ReactNode
} }
// eslint-disable-next-line react/display-name // eslint-disable-next-line react/display-name
@ -18,6 +19,7 @@ const Button = forwardRef<HTMLButtonElement, Props>(
buttonType = "primary", buttonType = "primary",
type = "button", type = "button",
disabled = false, disabled = false,
iconRight,
...props ...props
}, },
ref ref
@ -31,6 +33,11 @@ const Button = forwardRef<HTMLButtonElement, Props>(
{...props} {...props}
> >
{children} {children}
{iconRight && (
<span className={`${styles.icon} ${styles.iconRight}`}>
{iconRight}
</span>
)}
</button> </button>
) )
} }

View file

@ -1,16 +1,29 @@
import { File } from "@lib/types" import { File } from "@lib/types"
import { Card, Link, Text } from "@geist-ui/core"
import FileIcon from "@geist-ui/icons/fileText" import FileIcon from "@geist-ui/icons/fileText"
import CodeIcon from "@geist-ui/icons/fileLambda" import CodeIcon from "@geist-ui/icons/fileLambda"
import styles from "./file-tree.module.css" import styles from "./file-tree.module.css"
import ShiftBy from "@components/shift-by" import ShiftBy from "@components/shift-by"
import { useEffect, useState } from "react" import { useEffect, useState } from "react"
import { codeFileExtensions } from "@lib/constants" import { codeFileExtensions } from "@lib/constants"
import Link from "@components/link"
type Item = File & { type Item = File & {
icon: JSX.Element icon: JSX.Element
} }
const Card = ({
children,
className,
...props
}: {
children: React.ReactNode
className?: string
} & React.ComponentProps<"div">) => (
<div className={styles.card} {...props}>
{children}
</div>
)
const FileTree = ({ files }: { files: File[] }) => { const FileTree = ({ files }: { files: File[] }) => {
const [items, setItems] = useState<Item[]>([]) const [items, setItems] = useState<Item[]>([])
useEffect(() => { useEffect(() => {
@ -34,13 +47,13 @@ const FileTree = ({ files }: { files: File[] }) => {
// a list of files with an icon and a title // a list of files with an icon and a title
return ( return (
<div className={styles.fileTreeWrapper}> <div className={styles.fileTreeWrapper}>
<Card height={"100%"} className={styles.card}> <Card className={styles.card}>
<div className={styles.cardContent}> <div className={styles.cardContent}>
<Text h4>Files</Text> <h4>Files</h4>
<ul className={styles.fileTree}> <ul className={styles.fileTree}>
{items.map(({ id, title, icon }) => ( {items.map(({ id, title, icon }) => (
<li key={id}> <li key={id}>
<Link color={false} href={`#${title}`}> <Link href={`#${title}`}>
<ShiftBy y={5}> <ShiftBy y={5}>
<span className={styles.fileTreeIcon}>{icon}</span> <span className={styles.fileTreeIcon}>{icon}</span>
</ShiftBy> </ShiftBy>

View file

@ -1,3 +1,5 @@
'use client';
import { import {
ButtonGroup, ButtonGroup,
Button, Button,
@ -168,8 +170,7 @@ const Header = () => {
) )
} else if (tab.href) { } else if (tab.href) {
return ( return (
<Link key={tab.value} href={tab.href}> (<Link key={tab.value} href={tab.href} className={styles.tab}>
<a className={styles.tab}>
<Button <Button
className={activeStyle} className={activeStyle}
auto={isMobile ? false : true} auto={isMobile ? false : true}
@ -178,9 +179,9 @@ const Header = () => {
> >
{tab.name ? tab.name : undefined} {tab.name ? tab.name : undefined}
</Button> </Button>
</a>
</Link> </Link>)
) );
} }
}, },
[isMobile, onTabChange, router.pathname] [isMobile, onTabChange, router.pathname]

View file

@ -20,8 +20,8 @@ const Home = ({
<ShiftBy y={-2}> <ShiftBy y={-2}>
<Image <Image
src={"/assets/logo-optimized.svg"} src={"/assets/logo-optimized.svg"}
width={"48px"} width={48}
height={"48px"} height={48}
alt="" alt=""
/> />
</ShiftBy> </ShiftBy>

View file

@ -23,7 +23,7 @@
} }
.input::placeholder { .input::placeholder {
font-size: 1.5rem; font-size: 1rem;
} }
.input:focus { .input:focus {

View file

@ -0,0 +1,26 @@
import { useRouter } from "next/router"
import NextLink from "next/link"
import styles from "./link.module.css"
type LinkProps = {
href: string,
colored?: boolean,
children: React.ReactNode
} & React.ComponentProps<typeof NextLink>
const Link = ({ href, colored, children, ...props }: LinkProps) => {
const { basePath } = useRouter()
const propHrefWithoutLeadingSlash =
href && href.startsWith("/") ? href.substring(1) : href
const url = basePath ? `${basePath}/${propHrefWithoutLeadingSlash}` : href
const className = colored ? `${styles.link} ${styles.color}` : styles.link
return (
<NextLink {...props} href={url} className={className}>
{children}
</NextLink>
)
}
export default Link

View file

@ -0,0 +1,12 @@
.link {
text-decoration: none;
color: var(--fg);
}
.color {
color: var(--link);
}
.color:hover {
text-decoration: underline;
}

View file

@ -169,10 +169,7 @@ const Post = ({
setSubmitting(false) setSubmitting(false)
} }
const submitPassword = useCallback( const submitPassword = (password: string) => onSubmit("protected", password)
(password: string) => onSubmit("protected", password),
[onSubmit]
)
const onChangeExpiration = useCallback((date: Date) => setExpiresAt(date), []) const onChangeExpiration = useCallback((date: Date) => setExpiresAt(date), [])
@ -199,24 +196,17 @@ const Post = ({
[setDocs] [setDocs]
) )
const updateDocContent = useCallback( const updateDocContent = (i: number) => (content: string) => {
(i: number) => (content: string) => {
setDocs((docs) => setDocs((docs) =>
docs.map((doc, index) => (i === index ? { ...doc, content } : doc)) docs.map((doc, index) => (i === index ? { ...doc, content } : doc))
) )
}, }
[setDocs]
)
const removeDoc = useCallback( const removeDoc = (i: number) => () => {
(i: number) => () => {
setDocs((docs) => docs.filter((_, index) => i !== index)) setDocs((docs) => docs.filter((_, index) => i !== index))
}, }
[setDocs]
)
const uploadDocs = useCallback( const uploadDocs = (files: DocumentType[]) => {
(files: DocumentType[]) => {
// if no title is set and the only document is empty, // if no title is set and the only document is empty,
const isFirstDocEmpty = const isFirstDocEmpty =
docs.length <= 1 && (docs.length ? docs[0].title === "" : true) docs.length <= 1 && (docs.length ? docs[0].title === "" : true)
@ -231,9 +221,7 @@ const Post = ({
if (isFirstDocEmpty) setDocs(files) if (isFirstDocEmpty) setDocs(files)
else setDocs((docs) => [...docs, ...files]) else setDocs((docs) => [...docs, ...files])
}, }
[docs, title]
)
// pasted files // pasted files
// const files = e.clipboardData.files as File[] // const files = e.clipboardData.files as File[]
@ -340,15 +328,15 @@ const Post = ({
/> />
} }
<ButtonDropdown loading={isSubmitting} type="success"> <ButtonDropdown loading={isSubmitting} type="success">
<ButtonDropdown.Item onClick={() => onSubmit("unlisted")}>
Create Unlisted
</ButtonDropdown.Item>
<ButtonDropdown.Item main onClick={() => onSubmit("private")}> <ButtonDropdown.Item main onClick={() => onSubmit("private")}>
Create Private Create Private
</ButtonDropdown.Item> </ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("public")}> <ButtonDropdown.Item onClick={() => onSubmit("public")}>
Create Public Create Public
</ButtonDropdown.Item> </ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("unlisted")}>
Create Unlisted
</ButtonDropdown.Item>
<ButtonDropdown.Item onClick={() => onSubmit("protected")}> <ButtonDropdown.Item onClick={() => onSubmit("protected")}>
Create with Password Create with Password
</ButtonDropdown.Item> </ButtonDropdown.Item>

View file

@ -0,0 +1,17 @@
import styles from "./note.module.css"
const Note = ({
type = "info",
children,
...props
}: {
type: "info" | "warning" | "error"
children: React.ReactNode
} & React.ComponentProps<"div">) => (
<div className={`${styles.note} ${styles[type]}`} {...props}>
<strong className={styles.type}>{type}:</strong>
{children}
</div>
)
export default Note

View file

@ -0,0 +1,27 @@
.note {
font-size: 0.8em;
color: var(--fg);
margin: 0;
padding: var(--gap);
margin-top: 0.5em;
margin-bottom: 0.5em;
border-radius: var(--radius);
}
.info {
background: var(--gray);
}
.warning {
background: #f33;
}
.error {
background: red;
}
.type {
color: var(--fg);
margin-right: 0.5em;
text-transform: capitalize;
}

View file

@ -1,6 +1,4 @@
import { Button, Input, Select, Text } from "@geist-ui/core" import { Button, Input, Text } from "@geist-ui/core"
import NextLink from "next/link"
import Link from "../Link"
import styles from "./post-list.module.css" import styles from "./post-list.module.css"
import ListItemSkeleton from "./list-item-skeleton" import ListItemSkeleton from "./list-item-skeleton"
@ -9,6 +7,7 @@ import { Post } from "@lib/types"
import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react" import { ChangeEvent, useCallback, useEffect, useMemo, useState } from "react"
import Cookies from "js-cookie" import Cookies from "js-cookie"
import useDebounce from "@lib/hooks/use-debounce" import useDebounce from "@lib/hooks/use-debounce"
import Link from "@components/link"
type Props = { type Props = {
initialPosts: Post[] initialPosts: Post[]
@ -136,9 +135,9 @@ const PostList = ({ morePosts, initialPosts, error }: Props) => {
{posts?.length === 0 && !error && ( {posts?.length === 0 && !error && (
<Text type="secondary"> <Text type="secondary">
No posts found. Create one{" "} No posts found. Create one{" "}
<NextLink passHref={true} href="/new"> <Link colored href="/new">
<Link color>here</Link> here
</NextLink> </Link>
. .
</Text> </Text>
)} )}

View file

@ -1,14 +1,6 @@
import NextLink from "next/link" import NextLink from "next/link"
import VisibilityBadge from "../badges/visibility-badge" import VisibilityBadge from "../badges/visibility-badge"
import { import { Text, Card, Tooltip, Divider, Badge, Button } from "@geist-ui/core"
Link,
Text,
Card,
Tooltip,
Divider,
Badge,
Button
} from "@geist-ui/core"
import { File, Post } from "@lib/types" import { File, Post } from "@lib/types"
import FadeIn from "@components/fade-in" import FadeIn from "@components/fade-in"
import Trash from "@geist-ui/icons/trash" import Trash from "@geist-ui/icons/trash"
@ -18,6 +10,7 @@ import Edit from "@geist-ui/icons/edit"
import { useRouter } from "next/router" import { useRouter } from "next/router"
import Parent from "@geist-ui/icons/arrowUpCircle" import Parent from "@geist-ui/icons/arrowUpCircle"
import styles from "./list-item.module.css" import styles from "./list-item.module.css"
import Link from "@components/link"
// TODO: isOwner should default to false so this can be used generically // TODO: isOwner should default to false so this can be used generically
const ListItem = ({ const ListItem = ({
@ -45,15 +38,14 @@ const ListItem = ({
<Card style={{ overflowY: "scroll" }}> <Card style={{ overflowY: "scroll" }}>
<Card.Body> <Card.Body>
<Text h3 className={styles.title}> <Text h3 className={styles.title}>
<NextLink <Link
passHref={true} colored
style={{ marginRight: "var(--gap)" }}
href={`/post/[id]`} href={`/post/[id]`}
as={`/post/${post.id}`} as={`/post/${post.id}`}
> >
<Link color marginRight={"var(--gap)"}>
{post.title} {post.title}
</Link> </Link>
</NextLink>
{isOwner && ( {isOwner && (
<span className={styles.buttons}> <span className={styles.buttons}>
{post.parent && ( {post.parent && (
@ -97,7 +89,7 @@ const ListItem = ({
{post.files?.map((file: File) => { {post.files?.map((file: File) => {
return ( return (
<div key={file.id}> <div key={file.id}>
<Link color href={`/post/${post.id}#${file.title}`}> <Link colored href={`/post/${post.id}#${file.title}`}>
{file.title || "Untitled file"} {file.title || "Untitled file"}
</Link> </Link>
</div> </div>

View file

@ -3,16 +3,15 @@ import styles from "./document.module.css"
import Download from "@geist-ui/icons/download" import Download from "@geist-ui/icons/download"
import ExternalLink from "@geist-ui/icons/externalLink" import ExternalLink from "@geist-ui/icons/externalLink"
import Skeleton from "react-loading-skeleton" import Skeleton from "react-loading-skeleton"
import Link from 'next/link';
import { import {
Button, Button,
Text,
ButtonGroup, ButtonGroup,
Spacer, Spacer,
Tabs, Tabs,
Textarea, Textarea,
Tooltip, Tooltip,
Link,
Tag Tag
} from "@geist-ui/core" } from "@geist-ui/core"
import HtmlPreview from "@components/preview" import HtmlPreview from "@components/preview"
@ -32,7 +31,7 @@ const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
<div className={styles.actionWrapper}> <div className={styles.actionWrapper}>
<ButtonGroup className={styles.actions}> <ButtonGroup className={styles.actions}>
<Tooltip hideArrow text="Download"> <Tooltip hideArrow text="Download">
<a <Link
href={`${rawLink}?download=true`} href={`${rawLink}?download=true`}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
@ -44,10 +43,10 @@ const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
auto auto
aria-label="Download" aria-label="Download"
/> />
</a> </Link>
</Tooltip> </Tooltip>
<Tooltip hideArrow text="Open raw in new tab"> <Tooltip hideArrow text="Open raw in new tab">
<a href={rawLink} target="_blank" rel="noopener noreferrer"> <Link href={rawLink || ""} target="_blank" rel="noopener noreferrer">
<Button <Button
scale={2 / 3} scale={2 / 3}
px={0.6} px={0.6}
@ -55,7 +54,7 @@ const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
auto auto
aria-label="Open raw file in new tab" aria-label="Open raw file in new tab"
/> />
</a> </Link>
</Tooltip> </Tooltip>
</ButtonGroup> </ButtonGroup>
</div> </div>

View file

@ -16,7 +16,7 @@ export default function generateUUID() {
(crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4))) (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (num / 4)))
).toString(16) ).toString(16)
} }
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, callback) return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, callback);
} }
} }
let timestamp = new Date().getTime() let timestamp = new Date().getTime()
@ -35,5 +35,5 @@ export default function generateUUID() {
perforNow = Math.floor(perforNow / 16) perforNow = Math.floor(perforNow / 16)
} }
return (c === "x" ? random : (random & 0x3) | 0x8).toString(16) return (c === "x" ? random : (random & 0x3) | 0x8).toString(16)
}) });
} }

View file

@ -1,14 +1,14 @@
import { NextFetchEvent, NextRequest, NextResponse } from "next/server" import { NextFetchEvent, NextResponse } from "next/server"
import type { NextRequest } from "next/server"
const PUBLIC_FILE = /\.(.*)$/ const PUBLIC_FILE = /\.(.*)$/
export function middleware(req: NextRequest, event: NextFetchEvent) { export function middleware(req: NextRequest, event: NextFetchEvent) {
const pathname = req.nextUrl.pathname const pathname = req.nextUrl.pathname
const signedIn = req.cookies["drift-token"] const signedIn = req.cookies.get("drift-token")
const getURL = (pageName: string) => new URL(`/${pageName}`, req.url).href const getURL = (pageName: string) => new URL(`/${pageName}`, req.url).href
const isPageRequest = const isPageRequest =
!PUBLIC_FILE.test(pathname) && !PUBLIC_FILE.test(pathname) &&
!pathname.startsWith("/api") &&
// header added when next/link pre-fetches a route // header added when next/link pre-fetches a route
!req.headers.get("x-middleware-preflight") !req.headers.get("x-middleware-preflight")
@ -17,8 +17,8 @@ export function middleware(req: NextRequest, event: NextFetchEvent) {
// If you're not signed in we redirect to the home page // If you're not signed in we redirect to the home page
if (signedIn) { if (signedIn) {
const resp = NextResponse.redirect(getURL("")) const resp = NextResponse.redirect(getURL(""))
resp.clearCookie("drift-token") resp.cookies.delete("drift-token")
resp.clearCookie("drift-userid") resp.cookies.delete("drift-userid")
const signoutPromise = new Promise((resolve) => { const signoutPromise = new Promise((resolve) => {
fetch(`${process.env.API_URL}/auth/signout`, { fetch(`${process.env.API_URL}/auth/signout`, {
method: "POST", method: "POST",
@ -61,3 +61,17 @@ export function middleware(req: NextRequest, event: NextFetchEvent) {
return NextResponse.next() return NextResponse.next()
} }
export const config = {
match: [
"/signout",
"/",
"/signin",
"/signup",
"/new",
"/protected/:path*",
"/private/:path*"
]
}

View file

@ -7,8 +7,9 @@ dotenv.config()
const nextConfig = { const nextConfig = {
reactStrictMode: true, reactStrictMode: true,
experimental: { experimental: {
outputStandalone: true, // outputStandalone: true,
esmExternals: true esmExternals: true,
// appDir: true
}, },
webpack: (config, { dev, isServer }) => { webpack: (config, { dev, isServer }) => {
if (!dev && !isServer) { if (!dev && !isServer) {

View file

@ -11,7 +11,7 @@
"find:unused": "next-unused" "find:unused": "next-unused"
}, },
"dependencies": { "dependencies": {
"@geist-ui/core": "2.3.8", "@geist-ui/core": "^2.3.8",
"@geist-ui/icons": "1.0.2", "@geist-ui/icons": "1.0.2",
"@types/cookie": "0.5.1", "@types/cookie": "0.5.1",
"@types/js-cookie": "3.0.2", "@types/js-cookie": "3.0.2",
@ -19,13 +19,13 @@
"cookie": "0.5.0", "cookie": "0.5.0",
"dotenv": "16.0.0", "dotenv": "16.0.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.1",
"next": "12.1.6", "next": "13.0.2",
"next-themes": "0.2.0", "next-themes": "0.2.1",
"rc-table": "7.24.1", "rc-table": "7.24.1",
"react": "18.1.0", "react": "18.2.0",
"react-datepicker": "4.7.0", "react-datepicker": "4.8.0",
"react-dom": "18.1.0", "react-dom": "18.2.0",
"react-dropzone": "12.1.0", "react-dropzone": "14.2.3",
"react-loading-skeleton": "3.1.0", "react-loading-skeleton": "3.1.0",
"swr": "1.3.0", "swr": "1.3.0",
"textarea-markdown-editor": "0.1.13" "textarea-markdown-editor": "0.1.13"
@ -37,13 +37,16 @@
"@types/react-datepicker": "4.4.1", "@types/react-datepicker": "4.4.1",
"@types/react-dom": "18.0.3", "@types/react-dom": "18.0.3",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint": "8.15.0", "eslint": "8.27.0",
"eslint-config-next": "12.1.6", "eslint-config-next": "13.0.2",
"next-unused": "0.0.6", "next-unused": "0.0.6",
"prettier": "2.6.2", "prettier": "2.6.2",
"typescript": "4.6.4", "typescript": "4.6.4",
"typescript-plugin-css-modules": "3.4.0" "typescript-plugin-css-modules": "3.4.0"
}, },
"optionalDependencies": {
"sharp": "^0.31.2"
},
"next-unused": { "next-unused": {
"alias": { "alias": {
"@components": "components/", "@components": "components/",
@ -54,5 +57,8 @@
"components", "components",
"lib" "lib"
] ]
},
"overrides": {
"next": "13.0.2"
} }
} }

View file

@ -49,11 +49,9 @@ function MyApp({ Component, pageProps }: AppProps) {
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="#ffffff" />
<title>Drift</title> <title>Drift</title>
</Head> </Head>
<React.StrictMode>
<ThemeProvider defaultTheme="system" disableTransitionOnChange> <ThemeProvider defaultTheme="system" disableTransitionOnChange>
<App Component={Component} pageProps={pageProps} /> <App Component={Component} pageProps={pageProps} />
</ThemeProvider> </ThemeProvider>
</React.StrictMode>
</div> </div>
) )
} }

View file

@ -1,6 +1,5 @@
import styles from "@styles/Home.module.css" import styles from "@styles/Home.module.css"
import Header from "@components/header"
import { Page } from "@geist-ui/core" import { Page } from "@geist-ui/core"
import { useEffect } from "react" import { useEffect } from "react"
import Admin from "@components/admin" import Admin from "@components/admin"

View file

@ -1,4 +1,3 @@
import Header from "@components/header"
import { Note, Page, Text } from "@geist-ui/core" import { Note, Page, Text } from "@geist-ui/core"
import styles from "@styles/Home.module.css" import styles from "@styles/Home.module.css"

3740
client/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -48,8 +48,9 @@
--header-bg: rgba(19, 20, 21, 0.45); --header-bg: rgba(19, 20, 21, 0.45);
--gray-alpha: rgba(255, 255, 255, 0.5); --gray-alpha: rgba(255, 255, 255, 0.5);
--selection: rgba(255, 255, 255, 0.99); --selection: rgba(255, 255, 255, 0.99);
--border: var(--lighter-gray);
--warning: rgb(27, 134, 23); --warning: rgb(27, 134, 23);
--link: #3291ff;
} }
[data-theme="light"] { [data-theme="light"] {

View file

@ -1,8 +1,19 @@
{ {
"compilerOptions": { "compilerOptions": {
"plugins": [{ "name": "typescript-plugin-css-modules" }], "plugins": [
{
"name": "typescript-plugin-css-modules"
},
{
"name": "next"
}
],
"target": "es2020", "target": "es2020",
"lib": ["dom", "dom.iterable", "esnext"], "lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": true, "strict": true,
@ -27,11 +38,24 @@
"incremental": true, "incremental": true,
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@components/*": ["components/*"], "@components/*": [
"@lib/*": ["lib/*"], "components/*"
"@styles/*": ["styles/*"] ],
"@lib/*": [
"lib/*"
],
"@styles/*": [
"styles/*"
]
} }
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "include": [
"exclude": ["node_modules"] "next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
} }

File diff suppressed because it is too large Load diff

View file

@ -34,7 +34,7 @@
"reflect-metadata": "^0.1.10", "reflect-metadata": "^0.1.10",
"sequelize": "^6.17.0", "sequelize": "^6.17.0",
"sequelize-typescript": "^2.1.3", "sequelize-typescript": "^2.1.3",
"sqlite3": "https://github.com/mapbox/node-sqlite3#918052b538b0effe6c4a44c74a16b2749c08a0d2", "sqlite3": "^5.1.2",
"strong-error-handler": "^4.0.0", "strong-error-handler": "^4.0.0",
"umzug": "^3.1.0" "umzug": "^3.1.0"
}, },
@ -50,6 +50,7 @@
"@types/node-fetch": "2.6.1", "@types/node-fetch": "2.6.1",
"@types/react-dom": "17.0.16", "@types/react-dom": "17.0.16",
"@types/supertest": "2.0.12", "@types/supertest": "2.0.12",
"@types/validator": "^13.7.10",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"jest": "27.5.1", "jest": "27.5.1",
"prettier": "2.6.2", "prettier": "2.6.2",

4930
server/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff