Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web/react compiler #1109

Merged
merged 14 commits into from
Dec 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading