Skip to content

Commit

Permalink
Merge pull request #1 from firxworx/subpath-imports
Browse files Browse the repository at this point in the history
add subpath imports to react-layout, refactor/restructure workspace for clarity, bump packages
  • Loading branch information
firxworx authored Nov 23, 2024
2 parents 06f01f2 + 0770700 commit af9f97b
Show file tree
Hide file tree
Showing 37 changed files with 906 additions and 676 deletions.
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,11 @@ jobs:
id: install-dependencies
run: pnpm install --frozen-lockfile

- name: Copy .env.example to .env for CI build
run: |
cp .env.example .env
cp apps/client/.env.example apps/client/.env
- name: Run lint checks
id: eslint
run: pnpm ci:lint
Expand Down
18 changes: 9 additions & 9 deletions apps/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,36 @@
"@fluid-tailwind/tailwind-merge": "^0.0.3",
"@fontsource-variable/inter": "^5.1.0",
"@hookform/resolvers": "^3.9.1",
"@mantine/hooks": "^7.13.4",
"@mantine/hooks": "^7.14.1",
"@workspace/data": "workspace:*",
"@workspace/react-layout": "workspace:*",
"@workspace/react-ui": "workspace:*",
"@workspace/tw-style": "workspace:*",
"class-variance-authority": "^0.7.0",
"date-fns": "^4.1.0",
"lucide-react": "^0.454.0",
"lucide-react": "^0.460.0",
"react": "18.3.1",
"react-dom": "18.3.1",
"react-error-boundary": "^4.1.2",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.53.1",
"react-hook-form": "^7.53.2",
"react-router-dom": "^6.27.0",
"zod": "^3.23.8"
},
"devDependencies": {
"@csstools/postcss-oklab-function": "^4.0.5",
"@csstools/postcss-oklab-function": "^4.0.6",
"@testing-library/react": "^16.0.1",
"@types/react": "^18.3.12",
"@types/react-dom": "^18.3.1",
"@vitejs/plugin-react": "^4.3.3",
"@workspace/tw-preset-shadcn": "workspace:*",
"@workspace/tw-preset-workspace": "workspace:*",
"autoprefixer": "^10.4.20",
"fluid-tailwind": "^1.0.3",
"postcss": "^8.4.47",
"fluid-tailwind": "^1.0.4",
"postcss": "^8.4.49",
"postcss-discard-comments": "^7.0.3",
"tailwindcss": "^3.4.14",
"typescript": "^5.6.3",
"vite": "^5.4.10"
"tailwindcss": "^3.4.15",
"typescript": "^5.7.2",
"vite": "^5.4.11"
}
}
2 changes: 1 addition & 1 deletion apps/client/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { RouterProvider } from 'react-router-dom'
import { AppErrorBoundary } from '@workspace/react-layout'
import { ScreenSpinner } from '@workspace/react-ui/components/ui/spinner'

import { router } from './router'
import { router } from '@/router'
import { AppContextProviders } from '@/providers/AppContextProviders'

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useId } from 'react'

import { Heading } from '@workspace/react-ui/components/ui/heading'
import { cn } from '@workspace/tw-style'
import { LandingSectionHeader } from '#components/landing/landing.components.tsx'
import { LandingSectionHeader } from '@/components/landing/SharedComponents'

export interface FeaturesSectionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {}

Expand Down Expand Up @@ -58,6 +58,9 @@ export function FeaturesSection({ id, className, ...restProps }: FeaturesSection
)
}

/**
* List of features for landing FeaturesSection.
*/
function FeaturesList({ features, className }: FeaturesListProps): JSX.Element {
return (
<ul className={cn('flex flex-col justify-center ~gap-4/6', className)}>
Expand All @@ -73,6 +76,9 @@ function FeaturesList({ features, className }: FeaturesListProps): JSX.Element {
)
}

/**
* Mock/placeholder image frame.
*/
function MockImageFrame({ className, imgClassName }: { className?: string; imgClassName?: string }): JSX.Element {
return (
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { Heading } from '@workspace/react-ui/components/ui/heading'
export interface CTASectionProps extends Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> {}

/**
* CTA (call-to-action) section for index/landing page with newsletter sign-up form.
* Newsletter CTA (call-to-action) section for index/landing page with sign-up form.
*/
export function CTASection({ id, className, ...restProps }: CTASectionProps): JSX.Element {
export function NewsletterSection({ id, className, ...restProps }: CTASectionProps): JSX.Element {
const ssrId = useId()
const sectionId = id || ssrId

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@ export interface LandingSectionHeaderProps {
lead?: string
title: string
description?: string | React.ReactNode

leadVariant?: LeadProps['variant']
}

interface LeadProps {
export interface LeadProps {
variant: 'pill' | 'gradient'
children?: React.ReactNode
}
Expand Down Expand Up @@ -46,6 +45,9 @@ export function LandingSectionHeader({
)
}

/**
* Lead text to render above a landing section's primary heading.
*/
function Lead({ variant = 'pill', children }: LeadProps): JSX.Element | null {
if (!children) {
return null
Expand Down
32 changes: 17 additions & 15 deletions apps/client/src/config.ts
Original file line number Diff line number Diff line change
@@ -1,40 +1,39 @@
import { zSocialMediaDto } from '@workspace/data'
import { z } from 'zod'
import { zSocialMediaDto } from '@workspace/data'

export interface AppConfig extends z.infer<typeof zAppConfig> {}

export const IS_CLIENT = typeof globalThis?.window !== 'undefined'
export const IS_PRODUCTION = import.meta.env.PROD
export const IS_DEVELOPMENT = import.meta.env.DEV

/**
* API fetch timeout in milliseconds.
*/
export const API_FETCH_TIMEOUT_MS = 10000

/**
* Zod schema for this app's config object.
* Do not store sensitive information in this object because it is publicly available on the client.
* Do not store sensitive information in this object because it is publicly exposed to the client.
*
* The `locale` string must be valid BCP-47 format such as `en-US` or `fr-CA` despite only being
* validated as type `string`.
* The `locale` string must be valid BCP-47 format e.g. `en-US` or `fr-CA` although the schema only
* validates it as `string`.
*
* For reference the _recommended_ maximum lengths for fields related to SEO and meta are:
* **SEO & meta reference**
*
* The _recommended_ maximum lengths for fields related to SEO and meta are:
*
* - `title` <= 60 (at least for the most important parts to display on a search results page)
* - `description` <= 155
* - `keywords` < 10 comma separated keywords
*
* Important reminders for working with Vite:
* **Important reminders for working with Vite:**
*
* - `import.meta.env.BASE_URL` is defined based on the `base` property set in `vite.config.ts`
* - `import.meta.X` variables are "find and replace" with hardcoded-values-in-place during Vite build
* - `import.meta.env.BASE_URL` value is derived from the `base` property in `vite.config.ts`
* - `import.meta.X` variables are "find and replace" with hardcoded values-in-place during Vite build
* - `VITE_` prefix is required on environment variable names for Vite to expose them to browser/client environments
* - `process.env.X` is a Node-specific convention that does not exist in browser/client environments
*
* `vite.config.ts` has a custom `define` to replace `process.env.NODE_ENV` with a harcoded
* value based on the `NODE_ENV` value in play during the build process. This is done to enable any dependency
* code that relies on `process.env.NODE_ENV` to work as expected and not throw errors in the browser at runtime.
* value based on the `NODE_ENV` value in play during the build process. This step enables any dependencies
* that may rely on `process.env.NODE_ENV` to work as expected and not cause runtime errors in the browser.
*
* @see index.html for environment variable usage such as `%VITE_LOCALE%`
*/
export const zAppConfig = z.object({
title: z.string().default('App'),
Expand All @@ -60,6 +59,9 @@ export const zAppConfig = z.object({
// }),
})

/**
* This project's config object.
*/
export const CONFIG: AppConfig = zAppConfig.parse({
title: import.meta.env.VITE_APP_TITLE,
locale: import.meta.env.VITE_LOCALE,
Expand Down
19 changes: 19 additions & 0 deletions apps/client/src/layout/root.layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ScrollRestoration } from 'react-router-dom'
import { AppLayout } from '@workspace/react-layout/app.layout'

import { NAV_LINKS } from '@/nav'
import { CONFIG } from '@/config'

/**
* Root (core) layout component for this project.
*/
export function RootLayout(): JSX.Element {
return (
<>
<AppLayout navLinks={NAV_LINKS} socialMedia={CONFIG.socialMedia} />
<ScrollRestoration />
</>
)
}

export default RootLayout
6 changes: 3 additions & 3 deletions apps/client/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import ReactDOM from 'react-dom/client'

import '@fontsource-variable/inter'
import '@fontsource-variable/inter/opsz-italic.css'

import './style/tailwind.css'
import App from './App.tsx'
import { IS_CLIENT } from '@/config.ts'

import App from './App'
import { IS_CLIENT } from '@/config'

const mountNode = document.getElementById('root')

Expand Down
2 changes: 1 addition & 1 deletion apps/client/src/nav.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { AppNavLink, AppNavLinkGroup } from '@workspace/data'
import type { AppNavLink } from '@workspace/data'

export const NAV_LINKS: AppNavLink[] = [
{
Expand Down
42 changes: 26 additions & 16 deletions apps/client/src/pages/about.page.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,55 @@
import { Link } from 'react-router-dom'
import { PageLayout } from '@workspace/react-layout'
import { PageLayout } from '@workspace/react-layout/page.layout'

export default function AboutPage(): JSX.Element {
return (
<PageLayout className="prose">
<h1>About</h1>
<p>
This project workspace template was created by <Link to="https://firxworx.com/">@firxworx</Link> (
<Link to="https://github.com/firxworx">GitHub</Link>).
This project template was created by <Link to="https://firxworx.com/">@firxworx</Link> (
<Link to="https://github.com/firxworx">GitHub</Link>) in TypeScript and powered by{' '}
<Link to="https://vite.dev/">Vite</Link>.
</p>
<p>
It features <Link to="https://ui.shadcn.com/docs">shadcn/ui</Link>,{' '}
It includes <Link to="https://ui.shadcn.com/docs">shadcn/ui</Link> and CLI,{' '}
<Link to="https://www.radix-ui.com/">radix-ui</Link>,{' '}
<Link to="https://tailwindcss.com/docs/installation">tailwindcss</Link>, and the{' '}
<Link to="https://fluid.tw/">fluid-tailwind plugin</Link> showcased in an example{' '}
<Link to="https://react.dev/">react</Link> application with{' '}
<Link to="https://reactrouter.com/en/main">react-router</Link>.
<Link to="https://fluid.tw/">fluid-tailwind plugin</Link>.
</p>
<p>
Authored in <Link to="https://www.typescriptlang.org/">TypeScript</Link> and powered by{' '}
<Link to="https://vite.dev/">Vite</Link>.
A starter <Link to="https://react.dev/">React</Link> application with{' '}
<Link to="https://reactrouter.com/en/main">react-router</Link> demonstrates how everything in the workspace fits
together.
</p>
<p>
The code is organized in a <Link to="https://pnpm.io/workspaces">pnpm workspace</Link> (monorepo) with react
components, schemas & types (with <Link to="https://zod.dev/">zod</Link>), and{' '}
<Link to="https://tailwindcss.com/docs/presets">tailwind presets</Link> shared as internal packages (libraries)
that can be imported by multiple projects in the workspace.
The codebase is organized as a <Link to="https://pnpm.io/workspaces">pnpm workspace</Link> (monorepo) with
internal packages (libraries) housing shared react components, <Link to="https://zod.dev/">zod</Link> schemas,
types, and <Link to="https://tailwindcss.com/docs/presets">tailwind presets</Link> that can be used in multiple
projects across the workspace.
</p>
<p>
Linting and formatting by <Link to="https://biomejs.dev/">biome</Link>.
There are two tailwind presets: <code>tw-preset-shadcn</code> effectively "installs" shadcn/ui and{' '}
<code>tw-preset-workspace</code> defines the tailwind theme and customizations.
</p>
<p>
Performant linting and formatting is powered by <Link to="https://biomejs.dev/">biome</Link>.
</p>
<p>
A preview is deployed on <Link to="https://developers.cloudflare.com/pages/">Cloudflare Pages</Link>:
</p>
<div className="~p-4/6 rounded-md bg-muted/75 max-w-fit">
<div className="~p-4/6 rounded-md bg-muted border max-w-fit">
<Link to="https://vite-shadcn-workspace.pages.dev">https://vite-shadcn-workspace.pages.dev</Link>
</div>
<p>View the code on GitHub:</p>
<div className="~p-4/6 rounded-md bg-muted border max-w-fit">
<Link to="https://github.com/firxworx/vite-shadcn-workspace">
https://github.com/firxworx/vite-shadcn-workspace
</Link>
</div>
<h2>Acknowledgements</h2>
<h3>Design</h3>
<p>
The landing page design and components are roughly based on the boilerplate code generated by the{' '}
The landing page design and components are loosely based on the boilerplate generated by the{' '}
<Link to="https://kirimase.dev/">Kirimase CLI</Link> created by{' '}
<Link to="https://www.nicoalbanese.com/">Nico Albanese</Link>.
</p>
Expand Down
Loading

0 comments on commit af9f97b

Please sign in to comment.