feat: initial bootstrappy stuff

This commit is contained in:
Aarnav Tale 2024-03-25 17:50:58 -04:00
parent 70c42751e3
commit 025cc4f6f1
No known key found for this signature in database
12 changed files with 6697 additions and 11211 deletions

3
.eslintrc Normal file
View File

@ -0,0 +1,3 @@
{
"extends": "tale"
}

View File

@ -1,84 +0,0 @@
/**
* This is intended to be a basic starting point for linting in your app.
* It relies on recommended configs out of the box for simplicity, but you can
* and should modify this configuration to best suit your team's needs.
*/
/** @type {import('eslint').Linter.Config} */
module.exports = {
root: true,
parserOptions: {
ecmaVersion: "latest",
sourceType: "module",
ecmaFeatures: {
jsx: true,
},
},
env: {
browser: true,
commonjs: true,
es6: true,
},
ignorePatterns: ["!**/.server", "!**/.client"],
// Base config
extends: ["eslint:recommended"],
overrides: [
// React
{
files: ["**/*.{js,jsx,ts,tsx}"],
plugins: ["react", "jsx-a11y"],
extends: [
"plugin:react/recommended",
"plugin:react/jsx-runtime",
"plugin:react-hooks/recommended",
"plugin:jsx-a11y/recommended",
],
settings: {
react: {
version: "detect",
},
formComponents: ["Form"],
linkComponents: [
{ name: "Link", linkAttribute: "to" },
{ name: "NavLink", linkAttribute: "to" },
],
"import/resolver": {
typescript: {},
},
},
},
// Typescript
{
files: ["**/*.{ts,tsx}"],
plugins: ["@typescript-eslint", "import"],
parser: "@typescript-eslint/parser",
settings: {
"import/internal-regex": "^~/",
"import/resolver": {
node: {
extensions: [".ts", ".tsx"],
},
typescript: {
alwaysTryTypes: true,
},
},
},
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
],
},
// Node
{
files: [".eslintrc.cjs"],
env: {
node: true,
},
},
],
};

View File

@ -4,9 +4,9 @@
* For more information, see https://remix.run/file-conventions/entry.client * For more information, see https://remix.run/file-conventions/entry.client
*/ */
import { RemixBrowser } from "@remix-run/react"; import { RemixBrowser } from '@remix-run/react'
import { startTransition, StrictMode } from "react"; import { startTransition, StrictMode } from 'react'
import { hydrateRoot } from "react-dom/client"; import { hydrateRoot } from 'react-dom/client'
startTransition(() => { startTransition(() => {
hydrateRoot( hydrateRoot(
@ -14,5 +14,5 @@ startTransition(() => {
<StrictMode> <StrictMode>
<RemixBrowser/> <RemixBrowser/>
</StrictMode> </StrictMode>
); )
}); })

View File

@ -4,27 +4,24 @@
* For more information, see https://remix.run/file-conventions/entry.server * For more information, see https://remix.run/file-conventions/entry.server
*/ */
import { PassThrough } from "node:stream"; import { PassThrough } from 'node:stream'
import type { AppLoadContext, EntryContext } from "@remix-run/node"; import type { EntryContext } from '@remix-run/node'
import { createReadableStreamFromReadable } from "@remix-run/node"; import { createReadableStreamFromReadable } from '@remix-run/node'
import { RemixServer } from "@remix-run/react"; import { RemixServer } from '@remix-run/react'
import { isbot } from "isbot"; import { isbot } from 'isbot'
import { renderToPipeableStream } from "react-dom/server"; import { renderToPipeableStream } from 'react-dom/server'
const ABORT_DELAY = 5_000; // eslint-disable-next-line @typescript-eslint/naming-convention
const ABORT_DELAY = 5000
export default function handleRequest( export default async function handleRequest(
request: Request, request: Request,
responseStatusCode: number, responseStatusCode: number,
responseHeaders: Headers, responseHeaders: Headers,
remixContext: EntryContext, remixContext: EntryContext
// This is ignored so we can keep it in the template for visibility. Feel
// free to delete this parameter in your app if you're not using it!
// eslint-disable-next-line @typescript-eslint/no-unused-vars
loadContext: AppLoadContext
) { ) {
return isbot(request.headers.get("user-agent") || "") return isbot(request.headers.get('user-agent') ?? '')
? handleBotRequest( ? handleBotRequest(
request, request,
responseStatusCode, responseStatusCode,
@ -36,17 +33,17 @@ export default function handleRequest(
responseStatusCode, responseStatusCode,
responseHeaders, responseHeaders,
remixContext remixContext
); )
} }
function handleBotRequest( async function handleBotRequest(
request: Request, request: Request,
responseStatusCode: number, responseStatusCode: number,
responseHeaders: Headers, responseHeaders: Headers,
remixContext: EntryContext remixContext: EntryContext
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let shellRendered = false; let shellRendered = false
const { pipe, abort } = renderToPipeableStream( const { pipe, abort } = renderToPipeableStream(
<RemixServer <RemixServer
context={remixContext} context={remixContext}
@ -55,48 +52,48 @@ function handleBotRequest(
/>, />,
{ {
onAllReady() { onAllReady() {
shellRendered = true; shellRendered = true
const body = new PassThrough(); const body = new PassThrough()
const stream = createReadableStreamFromReadable(body); const stream = createReadableStreamFromReadable(body)
responseHeaders.set("Content-Type", "text/html"); responseHeaders.set('Content-Type', 'text/html')
resolve( resolve(
new Response(stream, { new Response(stream, {
headers: responseHeaders, headers: responseHeaders,
status: responseStatusCode, status: responseStatusCode
}) })
); )
pipe(body); pipe(body)
}, },
onShellError(error: unknown) { onShellError(error: unknown) {
reject(error); reject(error)
}, },
onError(error: unknown) { onError(error: unknown) {
responseStatusCode = 500; responseStatusCode = 500
// Log streaming rendering errors from inside the shell. Don't log // Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll // errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest. // reject and get logged in handleDocumentRequest.
if (shellRendered) { if (shellRendered) {
console.error(error); console.error(error)
} }
},
} }
); }
)
setTimeout(abort, ABORT_DELAY); setTimeout(abort, ABORT_DELAY)
}); })
} }
function handleBrowserRequest( async function handleBrowserRequest(
request: Request, request: Request,
responseStatusCode: number, responseStatusCode: number,
responseHeaders: Headers, responseHeaders: Headers,
remixContext: EntryContext remixContext: EntryContext
) { ) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let shellRendered = false; let shellRendered = false
const { pipe, abort } = renderToPipeableStream( const { pipe, abort } = renderToPipeableStream(
<RemixServer <RemixServer
context={remixContext} context={remixContext}
@ -105,36 +102,36 @@ function handleBrowserRequest(
/>, />,
{ {
onShellReady() { onShellReady() {
shellRendered = true; shellRendered = true
const body = new PassThrough(); const body = new PassThrough()
const stream = createReadableStreamFromReadable(body); const stream = createReadableStreamFromReadable(body)
responseHeaders.set("Content-Type", "text/html"); responseHeaders.set('Content-Type', 'text/html')
resolve( resolve(
new Response(stream, { new Response(stream, {
headers: responseHeaders, headers: responseHeaders,
status: responseStatusCode, status: responseStatusCode
}) })
); )
pipe(body); pipe(body)
}, },
onShellError(error: unknown) { onShellError(error: unknown) {
reject(error); reject(error)
}, },
onError(error: unknown) { onError(error: unknown) {
responseStatusCode = 500; responseStatusCode = 500
// Log streaming rendering errors from inside the shell. Don't log // Log streaming rendering errors from inside the shell. Don't log
// errors encountered during initial shell rendering since they'll // errors encountered during initial shell rendering since they'll
// reject and get logged in handleDocumentRequest. // reject and get logged in handleDocumentRequest.
if (shellRendered) { if (shellRendered) {
console.error(error); console.error(error)
} }
},
} }
); }
)
setTimeout(abort, ABORT_DELAY); setTimeout(abort, ABORT_DELAY)
}); })
} }

3
app/tailwind.css Normal file
View File

@ -0,0 +1,3 @@
@tailwind base;
@tailwind components;
@tailwind utilities;

10928
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
{ {
"name": "headplane-remix", "name": "headplane",
"private": true, "private": true,
"sideEffects": false, "sideEffects": false,
"type": "module", "type": "module",
@ -11,10 +11,13 @@
"typecheck": "tsc" "typecheck": "tsc"
}, },
"dependencies": { "dependencies": {
"@heroicons/react": "^2.1.3",
"@remix-run/node": "^2.8.1", "@remix-run/node": "^2.8.1",
"@remix-run/react": "^2.8.1", "@remix-run/react": "^2.8.1",
"@remix-run/serve": "^2.8.1", "@remix-run/serve": "^2.8.1",
"clsx": "^2.1.0",
"isbot": "^4.1.0", "isbot": "^4.1.0",
"oauth4webapi": "^2.10.3",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
@ -22,14 +25,11 @@
"@remix-run/dev": "^2.8.1", "@remix-run/dev": "^2.8.1",
"@types/react": "^18.2.20", "@types/react": "^18.2.20",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.7",
"@typescript-eslint/eslint-plugin": "^6.7.4", "autoprefixer": "^10.4.19",
"@typescript-eslint/parser": "^6.7.4", "eslint": "^8.57.0",
"eslint": "^8.38.0", "eslint-config-tale": "^1.0.16",
"eslint-import-resolver-typescript": "^3.6.1", "postcss": "^8.4.38",
"eslint-plugin-import": "^2.28.1", "tailwindcss": "^3.4.1",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.33.2",
"eslint-plugin-react-hooks": "^4.6.0",
"typescript": "^5.1.6", "typescript": "^5.1.6",
"vite": "^5.1.0", "vite": "^5.1.0",
"vite-tsconfig-paths": "^4.2.1" "vite-tsconfig-paths": "^4.2.1"

6477
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load Diff

6
postcss.config.js Normal file
View File

@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

10
tailwind.config.ts Normal file
View File

@ -0,0 +1,10 @@
import type { Config } from 'tailwindcss'
export default {
content: ['./app/**/*.{js,jsx,ts,tsx}'],
theme: {
extend: {}
},
plugins: []
} satisfies Config

View File

@ -1,10 +1,12 @@
import { vitePlugin as remix } from "@remix-run/dev"; import { vitePlugin as remix } from '@remix-run/dev'
import { installGlobals } from "@remix-run/node"; import { installGlobals } from '@remix-run/node'
import { defineConfig } from "vite"; import { defineConfig } from 'vite'
import tsconfigPaths from "vite-tsconfig-paths"; import tsconfigPaths from 'vite-tsconfig-paths'
installGlobals(); installGlobals()
export default defineConfig({ export default defineConfig({
plugins: [remix(), tsconfigPaths()], plugins: [remix({
}); basename: '/admin'
}), tsconfigPaths()]
})