Skip to content

Commit

Permalink
Web/react compiler (#1109)
Browse files Browse the repository at this point in the history
Signed-off-by: suddenlyGiovanni <[email protected]>
  • Loading branch information
suddenlyGiovanni authored Dec 13, 2024
1 parent f3dea8e commit 7004e70
Show file tree
Hide file tree
Showing 20 changed files with 2,577 additions and 775 deletions.
27 changes: 27 additions & 0 deletions apps/web/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import pluginReact from 'eslint-plugin-react'
import reactCompiler from 'eslint-plugin-react-compiler'
import globals from 'globals'
import tseslint from 'typescript-eslint'

/** @type {import('eslint').Linter.Config[]} */
export default [
{ files: ['**/*.{ts,tsx}'] },
{ languageOptions: { globals: { ...globals.browser, ...globals.node } } },
tseslint.configs.base,
pluginReact.configs.flat.recommended,
pluginReact.configs.flat['jsx-runtime'],
{
plugins: {
'react-compiler': reactCompiler,
},
rules: {
'react-compiler/react-compiler': 'error',
},
},
{
rules: {
'react/no-unescaped-entities': 'off',
'react/prop-types': [1, { skipUndeclared: true }],
},
},
]
11 changes: 10 additions & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
"react": "19.0.0",
"react-dom": "19.0.0",
"react-router": "7.0.2",
"remix-utils": "8.0.0"
"remix-utils": "8.0.0",
"vite-plugin-babel": "1.3.0"
},
"devDependencies": {
"@babel/preset-typescript": "7.26.0",
"@codecov/vite-plugin": "1.6.0",
"@eslint/js": "9.16.0",
"@react-router/dev": "7.0.2",
"@suddenlygiovanni/config-typescript": "workspace:*",
"@tailwindcss/typography": "0.5.15",
Expand All @@ -37,9 +40,15 @@
"@types/react-dom": "19.0.2",
"@vitest/coverage-v8": "3.0.0-beta.2",
"@vitest/ui": "3.0.0-beta.2",
"babel-plugin-react-compiler": "19.0.0-beta-37ed2a7-20241206",
"eslint": "9.16.0",
"eslint-plugin-react": "7.37.2",
"eslint-plugin-react-compiler": "19.0.0-beta-37ed2a7-20241206",
"globals": "15.13.0",
"react-router-devtools": "2.0.0-beta.0",
"tailwindcss": "4.0.0-beta.7",
"tailwindcss-animate": "1.0.7",
"typescript-eslint": "8.18.0",
"vite": "6.0.3",
"vite-tsconfig-paths": "5.1.4",
"vitest": "3.0.0-beta.2"
Expand Down
30 changes: 15 additions & 15 deletions apps/web/src/root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,6 @@ export function loader({ request }: Route.LoaderArgs) {
}
}

export default function App(_: Route.ComponentProps): ReactElement {
const { requestInfo } = useLoaderData<typeof loader>()
const theme = useTheme()

return (
<>
<Header theme={requestInfo.userPrefs.theme} />
<Main>
<Outlet />
</Main>
<Footer />
</>
)
}

export function Layout({ children }: { children: ReactNode }): ReactElement {
// if there was an error running the loader, data could be missing
const data = useLoaderData<typeof loader | null>()
Expand All @@ -97,4 +82,19 @@ export function Layout({ children }: { children: ReactNode }): ReactElement {
)
}

export default function App(_: Route.ComponentProps): ReactElement {
const { requestInfo } = useLoaderData<typeof loader>()
const theme = useTheme()

return (
<>
<Header theme={requestInfo.userPrefs.theme} />
<Main>
<Outlet />
</Main>
<Footer />
</>
)
}

export const ErrorBoundary = GeneralErrorBoundary
971 changes: 511 additions & 460 deletions apps/web/src/routes/resume/dev-icons.tsx

Large diffs are not rendered by default.

30 changes: 13 additions & 17 deletions apps/web/src/routes/resume/education.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Either, pipe } from 'effect'
import { type ReactElement, memo, useCallback, useMemo, useState } from 'react'
import { type MouseEventHandler, type ReactElement, useState } from 'react'

import type * as Model from '@suddenlygiovanni/resume/schema-resume'
import { Icons } from '@suddenlygiovanni/ui/components/icons/icons.tsx'
Expand All @@ -16,24 +16,20 @@ import { Button } from '@suddenlygiovanni/ui/ui/button.tsx'
import { formatDateLocaleShort } from './format-date-locale-short.ts'
import { generateDjb2Hash } from './generate-djb2-hash.ts'

export const Education = memo(function Education({
export function Education({
educations,
}: {
readonly educations: readonly Model.Education[]
}): ReactElement {
const all = useMemo(() => {
return educations.map((_, idx) => `education-${idx}`)
}, [educations])
const none = useMemo<string[]>(() => [], [])
const initialState = useMemo(() => {
const [head] = all
return head ? [head] : none
}, [all, none])
const all: string[] = educations.map((_, idx) => `education-${idx}`)
const none: string[] = []
const initialState: string[] = all[0] ? [all[0]] : none

const [value, setValue] = useState<string[]>(initialState)

const toggleEducation = useCallback(() => {
const toggleEducation: MouseEventHandler<HTMLButtonElement> = _ => {
setValue(prevState => (prevState.length > 0 ? none : all))
}, [all, none])
}

return (
<section className="relative w-full">
Expand Down Expand Up @@ -75,9 +71,9 @@ export const Education = memo(function Education({
</Accordion>
</section>
)
})
}

const Edu = memo(function Edu({
function Edu({
area,
courses,
endDate,
Expand Down Expand Up @@ -134,13 +130,13 @@ const Edu = memo(function Edu({
</dl>
</AccordionItem>
)
})
}

const styles = {
span: clsx('flex flex-row items-center font-normal text-sm accent-muted'),
} as const

const EduHeader = memo(function EduHeader({
function EduHeader({
area,
institution,
url,
Expand Down Expand Up @@ -244,4 +240,4 @@ const EduHeader = memo(function EduHeader({
</Trigger>
</dt>
)
})
}
10 changes: 5 additions & 5 deletions apps/web/src/routes/resume/experience.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type * as Model from '@suddenlygiovanni/resume/schema-resume'
import { Either, Option } from 'effect'
import { type ReactElement, memo } from 'react'
import type { ReactElement } from 'react'

import { Icons } from '@suddenlygiovanni/ui/components/icons/icons.tsx'
import { T } from '@suddenlygiovanni/ui/components/typography/typography.tsx'
Expand All @@ -13,7 +13,7 @@ import { Card } from '@suddenlygiovanni/ui/ui/card.tsx'
import { getDevIconComponent } from '~/routes/resume/dev-icons.tsx'
import { formatDateLocaleShort } from '~/routes/resume/format-date-locale-short.ts'

export const Experience = memo(function Experience(
export function Experience(
work: Pick<
Model.Work,
'contact' | 'description' | 'location' | 'name' | 'summary' | 'url' | 'roles'
Expand All @@ -40,7 +40,7 @@ export const Experience = memo(function Experience(
</article>
</AccordionItem>
)
})
}

const styles = {
span: clsx('flex flex-row items-center font-normal text-sm italic accent-muted'),
Expand All @@ -62,7 +62,7 @@ function getDates(roles: Model.Work['roles']): {
}
}

const ExperienceHeader = memo(function ExperienceHeader(work: {
function ExperienceHeader(work: {
readonly name: Model.Work['name']
readonly url: undefined | Model.Work['url']
readonly description: Model.Work['description']
Expand Down Expand Up @@ -162,7 +162,7 @@ const ExperienceHeader = memo(function ExperienceHeader(work: {
</Trigger>
</div>
)
})
}

function ExperienceSummary({
summary,
Expand Down
22 changes: 9 additions & 13 deletions apps/web/src/routes/resume/experiences.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { type ReactElement, memo, useCallback, useMemo, useState } from 'react'
import { type MouseEventHandler, type ReactElement, useState } from 'react'

import type * as Model from '@suddenlygiovanni/resume/schema-resume'
import { Icons } from '@suddenlygiovanni/ui/components/icons/icons.tsx'
Expand All @@ -8,24 +8,20 @@ import { Button } from '@suddenlygiovanni/ui/ui/button.tsx'

import { Experience } from './experience.tsx'

export const Experiences = memo(function Experiences({
export function Experiences({
work,
}: {
readonly work: readonly Model.Work[]
}): ReactElement {
const all = useMemo(() => {
return work.map((_, idx) => `experience-${idx}`)
}, [work])
const none = useMemo<string[]>(() => [], [])
const initialState = useMemo(() => {
const [head] = all
return head ? [head] : none
}, [all, none])
const all = work.map((_, idx) => `experience-${idx}`)
const none: string[] = []
const initialState: string[] = all[0] ? [all[0]] : none

const [value, setValue] = useState<string[]>(initialState)

const toggleExperiences = useCallback(() => {
const toggleExperiences: MouseEventHandler<HTMLButtonElement> = _ => {
setValue(prevState => (prevState.length > 0 ? none : all))
}, [all, none])
}

return (
<section className="relative w-full">
Expand Down Expand Up @@ -59,4 +55,4 @@ export const Experiences = memo(function Experiences({
</Accordion>
</section>
)
})
}
1 change: 0 additions & 1 deletion apps/web/src/routes/resume/generate-djb2-hash.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ export function generateDjb2Hash(s: string): string {
let hash = 5381
for (const char of s) {
const charCode = char.charCodeAt(0)
// eslint-disable-next-line no-bitwise -- DJB2 hash algorithm
hash = (hash << 5) + hash + charCode /* hash * 33 + c */
}
return hash.toString()
Expand Down
36 changes: 14 additions & 22 deletions apps/web/src/routes/resume/skills.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Option } from 'effect'
import { type ReactElement, memo, useCallback, useMemo, useState } from 'react'
import { type MouseEventHandler, type ReactElement, useState } from 'react'

import type { Skill as ResumeSkill } from '@suddenlygiovanni/resume/schema-resume'
import { Icons } from '@suddenlygiovanni/ui/components/icons/icons.tsx'
Expand All @@ -15,23 +15,19 @@ import { Button } from '@suddenlygiovanni/ui/ui/button.tsx'

import { getDevIconComponent } from './dev-icons.tsx'

export const Skills = memo(function Skills({
export function Skills({
skills,
}: {
readonly skills: readonly ResumeSkill[]
}): ReactElement {
const all = useMemo(() => skills.map((_, idx) => `skill-${idx}`), [skills])

const none = useMemo<string[]>(() => [], [])
const initialState = useMemo(() => {
const [head] = all
return head ? [head] : none
}, [all, none])
const all = skills.map((_, idx) => `skill-${idx}`)
const none: string[] = []
const initialState: string[] = all[0] ? [all[0]] : none
const [value, setValue] = useState<string[]>(initialState)

const toggleSkillsAccordion = useCallback(() => {
const toggleSkillsAccordion: MouseEventHandler<HTMLButtonElement> = _ => {
setValue(prevState => (prevState.length > 0 ? none : all))
}, [all, none])
}

return (
<section className="relative w-full">
Expand Down Expand Up @@ -67,13 +63,9 @@ export const Skills = memo(function Skills({
</Accordion>
</section>
)
})
}

const Skill = memo(function Skill({
name,
keywords,
value,
}: ResumeSkill & { readonly value: string }): ReactElement {
function Skill({ name, keywords, value }: ResumeSkill & { readonly value: string }): ReactElement {
return (
<AccordionItem
asChild={true}
Expand All @@ -100,9 +92,9 @@ const Skill = memo(function Skill({
</dl>
</AccordionItem>
)
})
}

const KeywordsList = memo(function KeywordsList({
function KeywordsList({
keywords,
}: {
readonly keywords: ResumeSkill['keywords']
Expand All @@ -123,9 +115,9 @@ const KeywordsList = memo(function KeywordsList({
</T.ul>
</AccordionContent>
)
})
}

const Keyword = memo(function Keyword({
function Keyword({
keyword,
}: {
readonly keyword: ResumeSkill['keywords'][number]
Expand All @@ -152,4 +144,4 @@ const Keyword = memo(function Keyword({
</li>
),
})
})
}
1 change: 0 additions & 1 deletion apps/web/src/utils/env.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ export function getEnv() {
export type Env = ReturnType<typeof getEnv>

declare global {
// eslint-disable-next-line no-var -- We need it to be hoisted and editable
var ENV: Env
interface Window {
// biome-ignore lint/style/useNamingConvention: <explanation>
Expand Down
13 changes: 13 additions & 0 deletions apps/web/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ import { codecovVitePlugin } from '@codecov/vite-plugin'
import { reactRouter } from '@react-router/dev/vite'
import tailwindcss from '@tailwindcss/vite'
import { reactRouterDevTools } from 'react-router-devtools'
import babel from 'vite-plugin-babel'
import tsconfigPaths from 'vite-tsconfig-paths'

/// <reference types="vitest/config" />
import { defineConfig } from 'vite'

const ReactCompilerConfig = {
/* ... */
}

export default defineConfig({
plugins: [
babel({
include: ['./src/**/*', '../../packages/ui/src/**/*'],
filter: name => name.endsWith('.tsx'),
babelConfig: {
presets: ['@babel/preset-typescript'], // if you use TypeScript
plugins: [['babel-plugin-react-compiler', ReactCompilerConfig]],
},
}),
reactRouterDevTools({
server: {
silent: false,
Expand Down
Loading

0 comments on commit 7004e70

Please sign in to comment.