├── .act └── event.json ├── .actrc ├── .devcontainer ├── Dockerfile ├── devcontainer.json ├── docker-compose.yml ├── docker-init.sh └── theme.omp.json ├── .dockerignore ├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── .lintstagedrc.json ├── .ncurc.json ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .syncpackrc ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── LICENSE ├── README.md ├── apps ├── astro │ ├── .env.development │ ├── .env.development.local │ ├── .env.keys │ ├── .env.prod │ ├── .gitignore │ ├── .lintstagedrc.json │ ├── .prettierignore │ ├── README.md │ ├── astro.config.ts │ ├── eslint.config.mjs │ ├── package.json │ ├── public │ │ └── favicon.ico │ ├── src │ │ ├── components │ │ │ └── .gitkeep │ │ ├── env.d.ts │ │ ├── layouts │ │ │ └── Layout.astro │ │ ├── pages │ │ │ ├── 404.astro │ │ │ ├── [...slug].astro │ │ │ └── articles │ │ │ │ └── [...slug].astro │ │ ├── payload.config.ts │ │ └── payload.types.ts │ ├── tailwind.config.ts │ └── tsconfig.json ├── docs │ ├── .lintstagedrc.json │ ├── .storybook │ │ ├── main.ts │ │ ├── preview-body.html │ │ ├── preview.tsx │ │ └── style.css │ ├── eslint.config.mjs │ ├── package.json │ ├── postcss.config.cjs │ ├── tailwind.config.ts │ └── tsconfig.json ├── next │ ├── .env.development │ ├── .env.development.local │ ├── .env.keys │ ├── .env.prod │ ├── .gitignore │ ├── .lintstagedrc.json │ ├── .prettierignore │ ├── README.md │ ├── docker-compose.yml │ ├── eslint.config.mjs │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.cjs │ ├── run-docker-compose.sh │ ├── src │ │ ├── app │ │ │ ├── (app) │ │ │ │ ├── (marketing) │ │ │ │ │ └── [[...slug]] │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── preview.tsx │ │ │ │ ├── (news) │ │ │ │ │ └── articles │ │ │ │ │ │ └── [slug] │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── preview.tsx │ │ │ │ ├── globals.css │ │ │ │ ├── layout.tsx │ │ │ │ └── not-found.tsx │ │ │ └── favicon.ico │ │ ├── payload.config.ts │ │ └── payload.types.ts │ ├── tailwind.config.ts │ └── tsconfig.json ├── payload │ ├── .env.development │ ├── .env.development.local │ ├── .env.keys │ ├── .env.prod │ ├── .gitignore │ ├── .lintstagedrc.json │ ├── .prettierignore │ ├── README.md │ ├── docker-compose.yml │ ├── eslint.config.mjs │ ├── next.config.ts │ ├── package.json │ ├── postcss.config.cjs │ ├── run-docker-compose.sh │ ├── src │ │ ├── app │ │ │ ├── (app) │ │ │ │ ├── (marketing) │ │ │ │ │ └── [[...slug]] │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── preview.tsx │ │ │ │ ├── (news) │ │ │ │ │ └── articles │ │ │ │ │ │ └── [slug] │ │ │ │ │ │ ├── page.tsx │ │ │ │ │ │ └── preview.tsx │ │ │ │ ├── globals.css │ │ │ │ ├── layout.tsx │ │ │ │ └── not-found.tsx │ │ │ ├── (payload) │ │ │ │ ├── admin │ │ │ │ │ ├── [[...segments]] │ │ │ │ │ │ ├── not-found.tsx │ │ │ │ │ │ └── page.tsx │ │ │ │ │ └── importMap.js │ │ │ │ ├── api │ │ │ │ │ ├── [...slug] │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── graphql-playground │ │ │ │ │ │ └── route.ts │ │ │ │ │ ├── graphql │ │ │ │ │ │ └── route.ts │ │ │ │ │ └── revalidate │ │ │ │ │ │ └── route.ts │ │ │ │ ├── custom.scss │ │ │ │ └── layout.tsx │ │ │ └── favicon.ico │ │ ├── payload.config.ts │ │ └── payload.types.ts │ ├── tailwind.config.ts │ └── tsconfig.json └── svelte │ ├── .env.development │ ├── .env.development.local │ ├── .env.keys │ ├── .env.prod │ ├── .gitignore │ ├── .lintstagedrc.json │ ├── .npmrc │ ├── .prettierignore │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── postcss.config.js │ ├── src │ ├── app.css │ ├── app.d.ts │ ├── app.html │ ├── lib │ │ └── index.ts │ └── routes │ │ ├── +layout.svelte │ │ └── [...slug] │ │ ├── +page.server.ts │ │ └── +page.svelte │ ├── static │ └── favicon.png │ ├── svelte.config.js │ ├── tailwind.config.ts │ ├── tsconfig.json │ └── vite.config.ts ├── docker-compose.override.yml ├── docker-compose.yml ├── eslint.config.mjs ├── package.json ├── packages ├── env │ ├── .lintstagedrc.json │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── app.ts │ │ ├── auth.ts │ │ ├── helpers │ │ │ └── skipValidation.ts │ │ ├── node.ts │ │ ├── payload.ts │ │ └── seo.ts │ └── tsconfig.json ├── icons │ ├── .lintstagedrc.json │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── CheckIcon │ │ │ └── index.tsx │ │ ├── InformationCircleIcon │ │ │ └── index.tsx │ │ ├── icons.stories.tsx │ │ ├── icons.ts │ │ └── index.tsx │ └── tsconfig.json ├── payload │ ├── .lintstagedrc.json │ ├── .prettierignore │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── access │ │ │ ├── AdminAccess │ │ │ │ └── index.ts │ │ │ ├── EveryoneAccess │ │ │ │ └── index.ts │ │ │ └── LoggedInAccess │ │ │ │ └── index.ts │ │ ├── blocks │ │ │ └── .gitkeep │ │ ├── collections │ │ │ ├── Articles │ │ │ │ └── index.ts │ │ │ ├── Media │ │ │ │ └── index.ts │ │ │ ├── Pages │ │ │ │ └── index.ts │ │ │ └── Users │ │ │ │ └── index.ts │ │ ├── components │ │ │ └── SlugField │ │ │ │ ├── index.scss │ │ │ │ ├── index.ts │ │ │ │ └── slug-field.tsx │ │ ├── configurePayload.ts │ │ ├── fields │ │ │ ├── address │ │ │ │ └── index.ts │ │ │ ├── body │ │ │ │ └── index.ts │ │ │ ├── eyebrow │ │ │ │ └── index.ts │ │ │ ├── heading │ │ │ │ └── index.ts │ │ │ ├── hideFromIndexing │ │ │ │ └── index.ts │ │ │ ├── image │ │ │ │ └── index.ts │ │ │ ├── link │ │ │ │ └── index.ts │ │ │ ├── media │ │ │ │ └── index.ts │ │ │ ├── publishedDate │ │ │ │ └── index.ts │ │ │ ├── richText │ │ │ │ └── index.ts │ │ │ ├── slug │ │ │ │ └── index.ts │ │ │ └── title │ │ │ │ └── index.ts │ │ ├── globals │ │ │ ├── Footer │ │ │ │ └── index.ts │ │ │ └── NavigationMenu │ │ │ │ └── index.ts │ │ ├── helpers │ │ │ └── .gitkeep │ │ ├── hooks │ │ │ ├── FormatSlugHook │ │ │ │ └── index.ts │ │ │ ├── PopulatePublishDateHook │ │ │ │ └── index.ts │ │ │ └── RevalidatePageHook │ │ │ │ └── index.ts │ │ ├── payload.config.ts │ │ ├── payload.types.ts │ │ └── plugins │ │ │ └── nestedDocsPlusPlugin │ │ │ └── index.ts │ └── tsconfig.json ├── svg │ ├── .lintstagedrc.json │ ├── README.md │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── Icon │ │ │ └── index.tsx │ │ └── Logo │ │ │ └── index.tsx │ └── tsconfig.json ├── types │ ├── .lintstagedrc.json │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── ComponentAsChild │ │ │ └── index.ts │ │ ├── ComponentAsChildGeneric │ │ │ └── index.ts │ │ ├── ExcludeNull │ │ │ └── index.ts │ │ ├── Prettify │ │ │ └── index.ts │ │ └── Redirect │ │ │ └── index.ts │ └── tsconfig.json ├── ui │ ├── .lintstagedrc.json │ ├── eslint.config.mjs │ ├── package.json │ ├── postcss.config.cjs │ ├── src │ │ ├── components │ │ │ └── .gitkeep │ │ ├── hooks │ │ │ └── .gitkeep │ │ ├── styles │ │ │ ├── index.css │ │ │ └── tailwind.css │ │ ├── types │ │ │ └── .gitkeep │ │ └── utils │ │ │ └── cn │ │ │ └── index.ts │ ├── tailwind.config.ts │ └── tsconfig.json └── utils │ ├── .lintstagedrc.json │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ ├── chunkArray │ │ └── index.ts │ ├── createSlug │ │ └── index.ts │ ├── fetchWithZod │ │ └── index.ts │ ├── isObject │ │ └── index.ts │ └── sleep │ │ └── index.ts │ └── tsconfig.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml ├── tooling ├── eslint │ ├── .lintstagedrc.json │ ├── astro.mjs │ ├── eslint.config.mjs │ ├── next.mjs │ ├── package.json │ ├── react.mjs │ ├── svelte.mjs │ └── tsconfig.json ├── github │ ├── .lintstagedrc.json │ ├── package.json │ └── setup │ │ └── action.yml ├── prettier │ ├── .lintstagedrc.json │ ├── index.js │ ├── package.json │ └── tsconfig.json ├── tailwind │ ├── eslint.config.mjs │ ├── package.json │ ├── src │ │ ├── colors.ts │ │ ├── tailwind.ts │ │ ├── types.ts │ │ ├── typography.ts │ │ └── ui.ts │ └── tsconfig.json └── typescript │ ├── .lintstagedrc.json │ ├── base.json │ ├── internal-package.json │ └── package.json └── turbo.json /.act/event.json: -------------------------------------------------------------------------------- 1 | { 2 | "act": true 3 | } 4 | -------------------------------------------------------------------------------- /.actrc: -------------------------------------------------------------------------------- 1 | -P ubuntu-latest=catthehacker/ubuntu:act-latest\ 2 | -P ubuntu-24.04=catthehacker/ubuntu:act-24.04 3 | -P ubuntu-22.04=catthehacker/ubuntu:act-22.04 4 | -P ubuntu-20.04=catthehacker/ubuntu:act-20.04 5 | -P ubuntu-18.04=catthehacker/ubuntu:act-18.04 6 | -e .act/event.json 7 | -------------------------------------------------------------------------------- /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM mcr.microsoft.com/devcontainers/base:ubuntu 2 | 3 | RUN curl -fsSL https://www.mongodb.org/static/pgp/server-8.0.asc | \ 4 | gpg -o /usr/share/keyrings/mongodb-server-8.0.gpg \ 5 | --dearmor 6 | RUN echo "deb [ arch=amd64,arm64 signed-by=/usr/share/keyrings/mongodb-server-8.0.gpg ] https://repo.mongodb.org/apt/ubuntu jammy/mongodb-org/8.0 multiverse" | tee /etc/apt/sources.list.d/mongodb-org-8.0.list 7 | 8 | RUN . /etc/os-release \ 9 | && apt-get update && export DEBIAN_FRONTEND=noninteractive \ 10 | && apt-get install -y mongodb-org-shell mongodb-org-tools \ 11 | && apt-get clean -y && rm -rf /var/lib/apt/lists/* 12 | 13 | RUN su vscode -c "curl -s https://ohmyposh.dev/install.sh | bash" 14 | 15 | RUN su vscode -c "curl -fsSL https://fnm.vercel.app/install | bash" 16 | 17 | COPY .devcontainer/theme.omp.json /usr/local/share/theme.omp.json 18 | COPY .devcontainer/docker-init.sh /usr/local/share/docker-init.sh 19 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/docker-outside-of-docker 3 | { 4 | "containerEnv": { 5 | "MEDUSA_REDIS_URL": "redis://localhost:6379" 6 | }, 7 | // Configure tool-specific properties. 8 | "customizations": { 9 | "vscode": { 10 | "extensions": [ 11 | "amazonwebservices.aws-toolkit-vscode", 12 | "bradlc.vscode-tailwindcss", 13 | "csstools.postcss", 14 | "dbaeumer.vscode-eslint", 15 | "esbenp.prettier-vscode", 16 | "formulahendry.auto-rename-tag", 17 | "ghmcadams.lintlens", 18 | "meganrogge.template-string-converter", 19 | "mikestead.dotenv", 20 | "ms-vsliveshare.vsliveshare", 21 | "pflannery.vscode-versionlens", 22 | "streetsidesoftware.code-spell-checker", 23 | "unifiedjs.vscode-mdx", 24 | "wix.vscode-import-cost", 25 | "DavidAnson.vscode-markdownlint", 26 | "YoavBls.pretty-ts-errors", 27 | "mongodb.mongodb-vscode", 28 | "naumovs.color-highlight" 29 | ], 30 | "settings": { 31 | "terminal.integrated.defaultProfile.linux": "zsh", 32 | "terminal.integrated.fontFamily": "FiraCode Nerd Font Mono" 33 | } 34 | } 35 | }, 36 | "dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"], 37 | "features": { 38 | "ghcr.io/devcontainers-extra/features/act:1": { 39 | "version": "latest" 40 | }, 41 | "ghcr.io/devcontainers-extra/features/zsh-plugins:0": { 42 | "omzPlugins": "https://github.com/zsh-users/zsh-autosuggestions", 43 | "plugins": "ssh-agent npm", 44 | "username": "vscode" 45 | }, 46 | "ghcr.io/devcontainers/features/aws-cli:1": { 47 | "version": "latest" 48 | }, 49 | "ghcr.io/devcontainers/features/terraform:1": { 50 | "terragrunt": "latest", 51 | "tflint": "latest", 52 | "version": "latest" 53 | }, 54 | "ghcr.io/stuartleeks/dev-container-features/shell-history:0": {} 55 | }, 56 | "forwardPorts": [3000, 3001, 4321, 5173], 57 | "mounts": [ 58 | "source=${env:HOME}${env:USERPROFILE}/.aws,target=/home/vscode/.aws,type=bind" 59 | ], 60 | "name": "Node.js & Mongo", 61 | "portsAttributes": { 62 | "3000": { 63 | "label": "Payload", 64 | "onAutoForward": "notify" 65 | } 66 | }, 67 | "postCreateCommand": "/usr/local/share/docker-init.sh", 68 | "service": "app", 69 | "waitFor": "postCreateCommand", 70 | "workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}" 71 | } 72 | -------------------------------------------------------------------------------- /.devcontainer/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | app: 3 | build: 4 | context: . 5 | dockerfile: .devcontainer/Dockerfile 6 | volumes: 7 | - ..:/workspaces:cached 8 | 9 | # Overrides default command so things don't shut down after the process ends. 10 | command: sleep infinity 11 | mongo: 12 | network_mode: service:app 13 | -------------------------------------------------------------------------------- /.devcontainer/docker-init.sh: -------------------------------------------------------------------------------- 1 | #! /bin/zsh 2 | 3 | echo 'eval "$(oh-my-posh init zsh --config '\''/usr/local/share/theme.omp.json'\'')"' >> ~/.zshrc 4 | echo 'eval "$(fnm env --version-file-strategy=recursive --corepack-enabled --resolve-engines --shell zsh)"' >> ~/.zshrc 5 | 6 | oh-my-posh font install FiraMono 7 | 8 | source ~/.zshrc 9 | 10 | fnm install 11 | fnm use 12 | 13 | pnpm i 14 | -------------------------------------------------------------------------------- /.devcontainer/theme.omp.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json", 3 | "blocks": [ 4 | { 5 | "alignment": "left", 6 | "segments": [ 7 | { 8 | "foreground": "#7eb8da", 9 | "style": "plain", 10 | "template": "\u250f{{ if .Error }}{{ .Error }}{{ else }}{{ if .Version }} {{.Version}}{{ end }}{{ if .Name }}[{{ .Name }}]{{ end }}{{ end }}", 11 | "type": "project" 12 | }, 13 | { 14 | "foreground": "#ffa5d8", 15 | "properties": { 16 | "fetch_stash_count": true, 17 | "fetch_status": true, 18 | "fetch_upstream_icon": true 19 | }, 20 | "style": "plain", 21 | "template": "[<#ffffff>{{ .UpstreamIcon }}{{ .HEAD }}{{if .BranchStatus }} {{ .BranchStatus }}{{ end }}{{ if .Working.Changed }} <#ffffff>\uf044 {{ .Working.String }}{{ end }}{{ if and (.Working.Changed) (.Staging.Changed) }} |{{ end }}{{ if .Staging.Changed }} <#ffffff>\uf046 {{ .Staging.String }}{{ end }}{{ if gt .StashCount 0 }} <#ffffff>\ueb4b {{ .StashCount }}{{ end }}]", 22 | "type": "git" 23 | }, 24 | { 25 | "foreground": "#98bfad", 26 | "style": "plain", 27 | "template": "[\ue718 {{ if .PackageManagerIcon }}{{ .PackageManagerIcon }} {{ end }}{{ .Full }}]", 28 | "type": "node" 29 | }, 30 | { 31 | "foreground": "#98bfad", 32 | "style": "plain", 33 | "template": "[\uDB80\uDEC1 {{.Full}}]", 34 | "type": "pnpm" 35 | }, 36 | { 37 | "foreground": "#ffa5d8", 38 | "template": "[{{ .WorkspaceName }}{{ if .Version }} {{ .Version }}{{ end }}]", 39 | "type": "terraform" 40 | }, 41 | { 42 | "foreground": "#bfa098", 43 | "style": "plain", 44 | "template": "[{{ .Profile }}{{if .Region}}@{{ .Region }}{{ end }}]", 45 | "type": "aws" 46 | }, 47 | { 48 | "foreground": "#ffa5d8", 49 | "style": "plain", 50 | "template": "[\uf0e7]", 51 | "type": "root" 52 | }, 53 | { 54 | "foreground": "#ffa5d8", 55 | "style": "powerline", 56 | "template": "[<#ffffff>\uea6c Error, check your command]", 57 | "type": "status" 58 | } 59 | ], 60 | "type": "prompt" 61 | }, 62 | { 63 | "alignment": "right", 64 | "segments": [ 65 | { 66 | "foreground": "#be9ddf", 67 | "properties": { 68 | "style": "dallas", 69 | "threshold": 0 70 | }, 71 | "style": "diamond", 72 | "template": "[<#ffffff>\uf252 {{ .FormattedMs }}s]", 73 | "type": "executiontime" 74 | } 75 | ], 76 | "type": "prompt" 77 | }, 78 | { 79 | "alignment": "left", 80 | "newline": true, 81 | "segments": [ 82 | { 83 | "foreground": "#7eb8da", 84 | "properties": { 85 | "mapped_locations": { 86 | "/workspaces": "@" 87 | }, 88 | "style": "full" 89 | }, 90 | "style": "plain", 91 | "template": "\u2516[<#98bfad>{{ .Path }}]", 92 | "type": "path" 93 | } 94 | ], 95 | "type": "prompt" 96 | }, 97 | { 98 | "alignment": "left", 99 | "newline": true, 100 | "segments": [ 101 | { 102 | "foreground": "#7eb8da", 103 | "style": "plain", 104 | "template": "\u2514\u2500Δ", 105 | "type": "text" 106 | } 107 | ], 108 | "type": "prompt" 109 | } 110 | ], 111 | "final_space": true, 112 | "version": 3 113 | } 114 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .next 3 | .vscode 4 | .github 5 | .turbo 6 | build 7 | debug 8 | dist 9 | bin 10 | .sst 11 | .open-next 12 | .turbo 13 | cdk* 14 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # CODEOWNERS 2 | * @fusionary/CODEOWNERS 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: Create a bug report to help us improve 3 | title: "bug: " 4 | labels: ["🐞❔ unconfirmed bug"] 5 | body: 6 | - type: textarea 7 | attributes: 8 | label: Provide environment information 9 | description: | 10 | Run this command in your project root and paste the results in a code block: 11 | ```bash 12 | npx envinfo --system --binaries 13 | ``` 14 | validations: 15 | required: true 16 | - type: textarea 17 | attributes: 18 | label: Describe the bug 19 | description: A clear and concise description of the bug, as well as what you expected to happen when encountering it. 20 | validations: 21 | required: true 22 | - type: input 23 | attributes: 24 | label: Link to reproduction 25 | description: Please provide a link to a reproduction of the bug. Issues without a reproduction repo may be ignored. 26 | validations: 27 | required: true 28 | - type: textarea 29 | attributes: 30 | label: To reproduce 31 | description: Describe how to reproduce your bug. Steps, code snippets, reproduction repos etc. 32 | validations: 33 | required: true 34 | - type: textarea 35 | attributes: 36 | label: Additional information 37 | description: Add any other information related to the bug here, screenshots if applicable. 38 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | # This template is heavily inspired by the Next.js's template: 2 | # See here: https://github.com/vercel/next.js/tree/canary/.github/ISSUE_TEMPLATE 3 | 4 | name: 🛠 Feature Request 5 | description: Create a feature request for the core packages 6 | title: "feat: " 7 | labels: ["✨ enhancement"] 8 | body: 9 | - type: markdown 10 | attributes: 11 | value: | 12 | Thank you for taking the time to file a feature request. Please fill out this form as completely as possible. 13 | - type: textarea 14 | attributes: 15 | label: Describe the feature you'd like to request 16 | description: Please describe the feature as clear and concise as possible. Remember to add context as to why you believe this feature is needed. 17 | validations: 18 | required: true 19 | - type: textarea 20 | attributes: 21 | label: Describe the solution you'd like to see 22 | description: Please describe the solution you would like to see. Adding example usage is a good way to provide context. 23 | validations: 24 | required: true 25 | - type: textarea 26 | attributes: 27 | label: Additional information 28 | description: Add any other information related to the feature here. If your feature request is related to any issues or discussions, link them here. 29 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for more information: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | # https://containers.dev/guide/dependabot 6 | 7 | version: 2 8 | updates: 9 | - package-ecosystem: "devcontainers" 10 | directory: "/" 11 | schedule: 12 | interval: weekly 13 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | branches: ['*'] 6 | push: 7 | branches: ['main'] 8 | merge_group: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} 13 | 14 | # You can leverage Vercel Remote Caching with Turbo to speed up your builds 15 | # @link https://turborepo.org/docs/core-concepts/remote-caching#remote-caching-on-vercel-builds 16 | env: 17 | FORCE_COLOR: 3 18 | 19 | jobs: 20 | lint: 21 | runs-on: ubuntu-latest 22 | steps: 23 | - uses: actions/checkout@v4 24 | 25 | - name: Setup 26 | uses: ./tooling/github/setup 27 | 28 | - name: Copy .env.example to .env 29 | shell: bash 30 | run: find . -name ".env.example" -type f -execdir cp -v {} .env \; 31 | 32 | - name: Lint 33 | run: pnpm lint && pnpm lint:ws 34 | 35 | format: 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | 40 | - name: Setup 41 | uses: ./tooling/github/setup 42 | 43 | - name: Format 44 | run: pnpm format 45 | 46 | typecheck: 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | 51 | - name: Setup 52 | uses: ./tooling/github/setup 53 | 54 | - name: Typecheck 55 | run: pnpm typecheck 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # next.js 12 | .next/ 13 | out/ 14 | next-env.d.ts 15 | 16 | # nitro 17 | .nitro/ 18 | .output/ 19 | 20 | # expo 21 | .expo/ 22 | expo-env.d.ts 23 | apps/expo/.gitignore 24 | 25 | # production 26 | build 27 | 28 | # misc 29 | .DS_Store 30 | *.pem 31 | 32 | # debug 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | .pnpm-debug.log* 37 | 38 | # local env files 39 | .env 40 | /.env.keys 41 | 42 | # vercel 43 | .vercel 44 | 45 | # typescript 46 | *.tsbuildinfo 47 | dist/ 48 | 49 | # turbo 50 | .turbo 51 | 52 | *storybook.log 53 | .parcel-cache 54 | 55 | local.db 56 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx lint-staged 2 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": ["prettier --write"], 3 | "*.{js,ts,tsx,cjs,mjs}": ["eslint --fix", "prettier --write"] 4 | } 5 | -------------------------------------------------------------------------------- /.ncurc.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://raw.githubusercontent.com/raineorshine/npm-check-updates/main/src/types/RunOptions.json", 3 | "deep": true, 4 | "enginesNode": true, 5 | "format": ["group", "repo", "time"], 6 | "packageManager": "pnpm", 7 | "peer": false, 8 | "target": "semver" 9 | } 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/.npmrc -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | payload.types.ts 2 | -------------------------------------------------------------------------------- /.syncpackrc: -------------------------------------------------------------------------------- 1 | { 2 | "versionGroups": [ 3 | { 4 | "label": "Use workspace protocol when developing local packages", 5 | "dependencies": ["$LOCAL"], 6 | "dependencyTypes": ["dev", "peer", "prod"], 7 | "pinVersion": "workspace:*" 8 | } 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "amazonwebservices.aws-toolkit-vscode", 4 | "bradlc.vscode-tailwindcss", 5 | "csstools.postcss", 6 | "dbaeumer.vscode-eslint", 7 | "esbenp.prettier-vscode", 8 | "formulahendry.auto-rename-tag", 9 | "ghmcadams.lintlens", 10 | "meganrogge.template-string-converter", 11 | "mikestead.dotenv", 12 | "ms-vsliveshare.vsliveshare", 13 | "pflannery.vscode-versionlens", 14 | "streetsidesoftware.code-spell-checker", 15 | "unifiedjs.vscode-mdx", 16 | "wix.vscode-import-cost", 17 | "DavidAnson.vscode-markdownlint", 18 | "YoavBls.pretty-ts-errors", 19 | "mongodb.mongodb-vscode", 20 | "naumovs.color-highlight" 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "compounds": [ 3 | { 4 | "configurations": ["Astro", "Payload"], 5 | "name": "Astro + Payload", 6 | "presentation": { 7 | "group": "Astro + Payload" 8 | }, 9 | "stopAll": true 10 | }, 11 | { 12 | "configurations": ["Next", "Payload"], 13 | "name": "Next + Payload", 14 | "presentation": { 15 | "group": "Next + Payload" 16 | }, 17 | "stopAll": true 18 | }, 19 | { 20 | "configurations": ["Svelte", "Payload"], 21 | "name": "Svelte + Payload", 22 | "presentation": { 23 | "group": "Svelte + Payload" 24 | }, 25 | "stopAll": true 26 | } 27 | ], 28 | "configurations": [ 29 | { 30 | "console": "integratedTerminal", 31 | "cwd": "${workspaceFolder}/", 32 | "name": "Astro", 33 | "request": "launch", 34 | "runtimeArgs": ["dev", "--filter", "astro..."], 35 | "runtimeExecutable": "pnpm", 36 | "skipFiles": ["/**"], 37 | "type": "node" 38 | }, 39 | { 40 | "console": "integratedTerminal", 41 | "cwd": "${workspaceFolder}/", 42 | "name": "Next", 43 | "request": "launch", 44 | "runtimeArgs": ["dev", "--filter", "next..."], 45 | "runtimeExecutable": "pnpm", 46 | "skipFiles": ["/**"], 47 | "type": "node" 48 | }, 49 | { 50 | "console": "integratedTerminal", 51 | "cwd": "${workspaceFolder}/", 52 | "name": "Payload", 53 | "request": "launch", 54 | "runtimeArgs": ["dev", "--filter", "payload..."], 55 | "runtimeExecutable": "pnpm", 56 | "skipFiles": ["/**"], 57 | "type": "node" 58 | }, 59 | { 60 | "console": "integratedTerminal", 61 | "cwd": "${workspaceFolder}/", 62 | "name": "Svelte", 63 | "request": "launch", 64 | "runtimeArgs": ["dev", "--filter", "svelte..."], 65 | "runtimeExecutable": "pnpm", 66 | "skipFiles": ["/**"], 67 | "type": "node" 68 | }, 69 | { 70 | "console": "integratedTerminal", 71 | "cwd": "${workspaceFolder}/", 72 | "name": "Docs", 73 | "request": "launch", 74 | "runtimeArgs": ["storybook"], 75 | "runtimeExecutable": "pnpm", 76 | "skipFiles": ["/**"], 77 | "type": "node" 78 | } 79 | ], 80 | "version": "0.2.0" 81 | } 82 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "[astro]": { 3 | "editor.defaultFormatter": "esbenp.prettier-vscode" 4 | }, 5 | "[css]": { 6 | "editor.defaultFormatter": "esbenp.prettier-vscode" 7 | }, 8 | "cSpell.words": [ 9 | "astro", 10 | "astrojs", 11 | "autoapply", 12 | "bigcommerce", 13 | "clsx", 14 | "Codegen", 15 | "deepmerge", 16 | "Embla", 17 | "Fusionary", 18 | "headlessui", 19 | "heroicons", 20 | "jiti", 21 | "Lucide", 22 | "Nextjs", 23 | "oidc", 24 | "oklch", 25 | "payloadcms", 26 | "RESIZER", 27 | "Skus", 28 | "tada", 29 | "tailwind", 30 | "tailwindcss", 31 | "Terrateam", 32 | "tsbuildinfo", 33 | "tsup", 34 | "turborepo" 35 | ], 36 | "debug.console.closeOnEnd": true, 37 | "editor.codeActionsOnSave": { 38 | "source.fixAll.eslint": "explicit", 39 | "source.formatDocument": "explicit" 40 | }, 41 | "editor.defaultFormatter": "esbenp.prettier-vscode", 42 | "editor.formatOnSave": true, 43 | "editor.tabSize": 2, 44 | "eslint.rules.customizations": [{ "rule": "*", "severity": "warn" }], 45 | "eslint.validate": [ 46 | "javascript", 47 | "javascriptreact", 48 | "astro", // Enable .astro 49 | "typescript", // Enable .ts 50 | "typescriptreact" // Enable .tsx 51 | ], 52 | "eslint.workingDirectories": [ 53 | { "pattern": "apps/*/" }, 54 | { "pattern": "packages/*/" }, 55 | { "pattern": "tooling/*/" } 56 | ], 57 | "files.exclude": { 58 | "**/.DS_Store": true, 59 | "**/.astro": true, 60 | "**/.bundle": true, 61 | "**/.cache": true, 62 | "**/.git": true, 63 | "**/.husky": true, 64 | "**/.idea": true, 65 | "**/.next": true, 66 | "**/.open-next": true, 67 | "**/.parcel-cache": true, 68 | "**/.sst": true, 69 | "**/.svelte-kit": true, 70 | "**/.turbo": true, 71 | "**/Thumbs.db": true, 72 | "**/build": true, 73 | "**/dist": true, 74 | "**/node_modules": true 75 | }, 76 | "files.insertFinalNewline": true, 77 | "prettier.documentSelectors": ["**/*.astro"], 78 | "tailwindCSS.experimental.classRegex": [ 79 | ["cva\\(([^)]*)\\)", "[\"'`]([^\"'`]*).*?[\"'`]"], 80 | ["cx\\(([^)]*)\\)", "(?:'|\"|`)([^']*)(?:'|\"|`)"], 81 | ["(?:twMerge|twJoin|cn)\\(([^\\);]*)[\\);]", "[`'\"`]([^'\"`,;]*)[`'\"`]"], 82 | ["className[=:]\\s*", "[\"'`]([^\"'`]*)[\"'`]"] 83 | ], 84 | "tailwindCSS.experimental.configFile": "./tooling/tailwind/src/tailwind.ts", 85 | "typescript.enablePromptUseWorkspaceTsdk": true, 86 | "typescript.preferences.autoImportFileExcludePatterns": [ 87 | "next/router.d.ts", 88 | "next/dist/client/router.d.ts" 89 | ], 90 | "typescript.tsdk": "node_modules/typescript/lib" 91 | } 92 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": [ 3 | { 4 | "args": ["i"], 5 | "command": "pnpm", 6 | "group": "none", 7 | "label": "pnpm install", 8 | "presentation": { 9 | "close": true, 10 | "panel": "new", 11 | "reveal": "always", 12 | "revealProblems": "onProblem", 13 | "showReuseMessage": false 14 | }, 15 | "type": "process" 16 | }, 17 | { 18 | "args": ["payload", "generate:types"], 19 | "command": "pnpm", 20 | "group": "none", 21 | "label": "generate types", 22 | "options": { 23 | "cwd": "${workspaceFolder}/apps/payload" 24 | }, 25 | "presentation": { 26 | "close": true, 27 | "panel": "new", 28 | "reveal": "always", 29 | "revealProblems": "onProblem", 30 | "showReuseMessage": false 31 | }, 32 | "type": "process" 33 | }, 34 | { 35 | "args": ["payload", "generate:importMap"], 36 | "command": "pnpm", 37 | "group": "none", 38 | "label": "generate import map", 39 | "options": { 40 | "cwd": "${workspaceFolder}/apps/payload" 41 | }, 42 | "presentation": { 43 | "close": true, 44 | "panel": "new", 45 | "reveal": "always", 46 | "revealProblems": "onProblem", 47 | "showReuseMessage": false 48 | }, 49 | "type": "process" 50 | } 51 | ], 52 | "version": "2.0.0" 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) [2024] [Fusionary, LLC] 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /apps/astro/.env.development: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | -------------------------------------------------------------------------------- /apps/astro/.env.development.local: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | 7 | # .env.development 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI="mongodb://127.0.0.1/payload-mono" 10 | PAYLOAD_PRIVATE_DATABASE_ENGINE='mongo' 11 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 12 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 13 | DISABLE_PAYLOAD_HMR=true 14 | 15 | # NEXTJS 16 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 17 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 18 | -------------------------------------------------------------------------------- /apps/astro/.env.keys: -------------------------------------------------------------------------------- 1 | ../../.env.keys -------------------------------------------------------------------------------- /apps/astro/.env.prod: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_PROD="03e6947395db98e04b2c1ac57b82744425224746d70ffe8b61d6920cff4e0ac724" 6 | 7 | # .env.prod 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI=mongodb://127.0.0.1:27017/cms 10 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 11 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 12 | DISABLE_PAYLOAD_HMR=true 13 | 14 | # NEXTJS 15 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 16 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 17 | -------------------------------------------------------------------------------- /apps/astro/.gitignore: -------------------------------------------------------------------------------- 1 | # build output 2 | dist/ 3 | 4 | # generated types 5 | .astro/ 6 | 7 | # dependencies 8 | node_modules/ 9 | 10 | # logs 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # environment variables 17 | .env 18 | .env.production 19 | 20 | # macOS-specific files 21 | .DS_Store 22 | 23 | # jetbrains setting folder 24 | .idea/ 25 | -------------------------------------------------------------------------------- /apps/astro/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/astro/.prettierignore: -------------------------------------------------------------------------------- 1 | .astro/* 2 | -------------------------------------------------------------------------------- /apps/astro/README.md: -------------------------------------------------------------------------------- 1 | # Astro Starter Kit: Basics 2 | 3 | ```sh 4 | npm create astro@latest -- --template basics 5 | ``` 6 | 7 | [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) 8 | [![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) 9 | [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) 10 | 11 | > 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 12 | 13 | ![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) 14 | 15 | ## 🚀 Project Structure 16 | 17 | Inside of your Astro project, you'll see the following folders and files: 18 | 19 | ```text 20 | / 21 | ├── public/ 22 | │ └── favicon.svg 23 | ├── src/ 24 | │ ├── components/ 25 | │ │ └── Card.astro 26 | │ ├── layouts/ 27 | │ │ └── Layout.astro 28 | │ └── pages/ 29 | │ └── index.astro 30 | └── package.json 31 | ``` 32 | 33 | Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. 34 | 35 | There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. 36 | 37 | Any static assets, like images, can be placed in the `public/` directory. 38 | 39 | ## 🧞 Commands 40 | 41 | All commands are run from the root of the project, from a terminal: 42 | 43 | | Command | Action | 44 | | :------------------------ | :----------------------------------------------- | 45 | | `npm install` | Installs dependencies | 46 | | `npm run dev` | Starts local dev server at `localhost:4321` | 47 | | `npm run build` | Build your production site to `./dist/` | 48 | | `npm run preview` | Preview your build locally, before deploying | 49 | | `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | 50 | | `npm run astro -- --help` | Get help using the Astro CLI | 51 | 52 | ## 👀 Want to learn more? 53 | 54 | Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). 55 | -------------------------------------------------------------------------------- /apps/astro/astro.config.ts: -------------------------------------------------------------------------------- 1 | import node from '@astrojs/node' 2 | import react from '@astrojs/react' 3 | import tailwind from '@astrojs/tailwind' 4 | import { defineConfig } from 'astro/config' 5 | 6 | // https://astro.build/config 7 | export default defineConfig({ 8 | adapter: node({ 9 | mode: 'standalone', 10 | }), 11 | integrations: [react({}), tailwind({ applyBaseStyles: true })], 12 | output: 'server', 13 | vite: { 14 | ssr: { 15 | noExternal: ['@payloadcms/ui'], 16 | }, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /apps/astro/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/astro' 2 | -------------------------------------------------------------------------------- /apps/astro/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "astro", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "scripts": { 6 | "astro": "astro", 7 | "build": "astro check && astro build", 8 | "dev": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation --inspect\" astro dev --host 0.0.0.0", 9 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 10 | "lint": "eslint .", 11 | "preview": "astro preview", 12 | "start": "astro dev", 13 | "typecheck": "tsc --noEmit" 14 | }, 15 | "prettier": "@local/prettier-config", 16 | "dependencies": { 17 | "@astrojs/check": "^0.9.4", 18 | "@astrojs/node": "^9.1.2", 19 | "@astrojs/react": "4.1.2", 20 | "@astrojs/tailwind": "^5.1.5", 21 | "@astrojs/ts-plugin": "^1.10.4", 22 | "@dotenvx/dotenvx": "^1.38.4", 23 | "@local/env": "workspace:*", 24 | "@local/eslint-config": "workspace:*", 25 | "@local/payload": "workspace:*", 26 | "@local/prettier-config": "workspace:*", 27 | "@local/tailwind-config": "workspace:*", 28 | "@local/tsconfig": "workspace:*", 29 | "@local/types": "workspace:*", 30 | "@types/node": "^20.17.23", 31 | "astro": "^5.4.2", 32 | "cross-env": "^7.0.3", 33 | "eslint": "^9.21.0", 34 | "payload": "^3.27.0", 35 | "prettier": "^3.5.3", 36 | "react": "catalog:next15", 37 | "react-dom": "catalog:next15", 38 | "react-image-crop": "10.1.8", 39 | "tailwindcss": "^3.4.17", 40 | "typescript": "^5.8.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /apps/astro/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/astro/public/favicon.ico -------------------------------------------------------------------------------- /apps/astro/src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/astro/src/components/.gitkeep -------------------------------------------------------------------------------- /apps/astro/src/env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /apps/astro/src/layouts/Layout.astro: -------------------------------------------------------------------------------- 1 | --- 2 | type Props = { 3 | description?: string 4 | title: string 5 | } 6 | 7 | const { description, title } = Astro.props 8 | --- 9 | 10 | 11 | 12 | 13 | 14 | {description && } 15 | 16 | 17 | 18 | {title} | Payload Monorepo Template 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /apps/astro/src/pages/404.astro: -------------------------------------------------------------------------------- 1 | --- 2 | import Layout from '~layouts/Layout.astro' 3 | 4 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unused-vars 5 | const props = Astro.props 6 | --- 7 | 8 | 9 |
10 |
11 |

404

12 |

15 | Page not found 16 |

17 |

18 | Sorry, we couldn’t find the page you’re looking for. 19 |

20 |
21 | {} 22 | 26 | Go back home 27 | 28 |
29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /apps/astro/src/pages/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import config from '@payload-config' 3 | import Layout from '~layouts/Layout.astro' 4 | import { getPayload } from 'payload' 5 | 6 | export const prerender = true 7 | 8 | export async function getStaticPaths() { 9 | const payload = await getPayload({ config }) 10 | const pageDocs = await payload.find({ collection: 'pages' }) 11 | return pageDocs.docs.map(({ meta: { title } = {}, slug }) => ({ 12 | params: { slug: slug === '' ? undefined : slug }, 13 | props: { text: title, title }, 14 | })) 15 | } 16 | 17 | const payload = await getPayload({ config }) 18 | 19 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unused-vars 20 | const props = Astro.props 21 | 22 | const data = await payload.find({ 23 | collection: 'pages', 24 | draft: true, 25 | limit: 1, 26 | where: { pathname: { equals: `/${Astro.params.slug ?? ''}` } }, 27 | }) 28 | 29 | const doc = data.docs?.[0] 30 | 31 | if (!doc) { 32 | return Astro.redirect('/404') 33 | } 34 | 35 | const { title: text, meta: { title } = {} } = doc 36 | --- 37 | 38 | 39 |

{text}

40 |
41 | -------------------------------------------------------------------------------- /apps/astro/src/pages/articles/[...slug].astro: -------------------------------------------------------------------------------- 1 | --- 2 | import config from '@payload-config' 3 | import Layout from '~layouts/Layout.astro' 4 | import { getPayload } from 'payload' 5 | 6 | export async function getStaticPaths() { 7 | const payload = await getPayload({ config }) 8 | const pageDocs = await payload.find({ collection: 'articles' }) 9 | return pageDocs.docs.map(({ meta: { title } = {}, slug }) => ({ 10 | params: { slug: slug === '' ? undefined : slug }, 11 | props: { text: title, title }, 12 | })) 13 | } 14 | 15 | const payload = await getPayload({ config }) 16 | 17 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unused-vars 18 | const props = Astro.props 19 | 20 | const data = await payload.find({ 21 | collection: 'articles', 22 | draft: true, 23 | limit: 1, 24 | where: { slug: { equals: Astro.params.slug } }, 25 | }) 26 | 27 | const doc = data.docs?.[0] 28 | 29 | if (!doc) { 30 | return Astro.redirect('/404') 31 | } 32 | 33 | const { title: text, meta: { title } = {} } = doc 34 | --- 35 | 36 | 37 |

{text}

38 |
39 | -------------------------------------------------------------------------------- /apps/astro/src/payload.config.ts: -------------------------------------------------------------------------------- 1 | import { configurePayload } from '@local/payload/configurePayload' 2 | 3 | export default configurePayload() 4 | -------------------------------------------------------------------------------- /apps/astro/src/payload.types.ts: -------------------------------------------------------------------------------- 1 | export * from '@local/payload/payload-types' 2 | -------------------------------------------------------------------------------- /apps/astro/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from 'tailwindcss' 6 | 7 | import baseConfig from '@local/tailwind-config' 8 | 9 | export default { 10 | content: [ 11 | ...baseConfig.content, 12 | '../../packages/*/src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', 13 | ], 14 | presets: [baseConfig], 15 | } satisfies Config 16 | -------------------------------------------------------------------------------- /apps/astro/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "paths": { 5 | "@payload-config": ["./src/payload.config.ts"], 6 | "@payload-types": ["./src/payload.types.ts"], 7 | "~/*": ["./src/*"], 8 | "~layouts/*": ["./src/layouts/*"] 9 | }, 10 | "plugins": [ 11 | { 12 | "name": "@astrojs/ts-plugin" 13 | } 14 | ], 15 | "verbatimModuleSyntax": true 16 | }, 17 | "exclude": ["node_modules", "src/env.d.ts"], 18 | "extends": "astro/tsconfigs/strict", 19 | "include": [".", "*.astro"] 20 | } 21 | -------------------------------------------------------------------------------- /apps/docs/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/docs/.storybook/main.ts: -------------------------------------------------------------------------------- 1 | import type { StorybookConfig } from '@fusionary/storybook-config/main' 2 | import { config as base } from '@fusionary/storybook-config/main' 3 | 4 | export const config: StorybookConfig = { 5 | ...base, 6 | stories: [ 7 | '../../../packages/*/src/**/*.mdx', 8 | '../../../packages/*/src/**/*.stories.@(js|jsx|mjs|ts|tsx)', 9 | ], 10 | } 11 | 12 | export default config 13 | -------------------------------------------------------------------------------- /apps/docs/.storybook/preview-body.html: -------------------------------------------------------------------------------- 1 | 8 | -------------------------------------------------------------------------------- /apps/docs/.storybook/preview.tsx: -------------------------------------------------------------------------------- 1 | import type { Preview } from '@fusionary/storybook-config/preview' 2 | import basePreview from '@fusionary/storybook-config/preview' 3 | 4 | import '@fusionary/storybook-config/style.css' 5 | import './style.css' 6 | import '@local/ui/styles.css' 7 | 8 | const parameters = { 9 | ...basePreview.parameters, 10 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 11 | options: { 12 | ...basePreview.parameters?.options, 13 | storySort: { 14 | method: 'alphabetical', 15 | order: [ 16 | 'Layouts', 17 | 'Pages', 18 | 'Sections', 19 | 'Typography', 20 | 'Elements', 21 | 'Layout', 22 | 'Forms', 23 | 'Icons', 24 | ], 25 | }, 26 | }, 27 | } 28 | 29 | const preview: Preview = { 30 | ...basePreview, 31 | parameters, 32 | } 33 | 34 | export default preview 35 | -------------------------------------------------------------------------------- /apps/docs/.storybook/style.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/docs/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/react' 2 | -------------------------------------------------------------------------------- /apps/docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "commonjs", 7 | "scripts": { 8 | "build-storybook": "storybook build", 9 | "clean": "rm -rf .turbo node_modules .next", 10 | "format": "prettier --check . --ignore-path ../../.gitignore", 11 | "lint": "eslint .", 12 | "storybook": "storybook dev -p 6006", 13 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 14 | }, 15 | "prettier": "@local/prettier-config", 16 | "dependencies": { 17 | "@fusionary/storybook-config": "^5.1.6", 18 | "@local/ui": "workspace:*", 19 | "@storybook/addon-a11y": "^8.6.4", 20 | "@storybook/addon-designs": "^8.2.0", 21 | "@storybook/addon-essentials": "^8.6.4", 22 | "@storybook/addon-interactions": "^8.6.4", 23 | "@storybook/addon-links": "^8.6.4", 24 | "@storybook/addon-onboarding": "^8.6.4", 25 | "@storybook/addon-viewport": "^8.6.4", 26 | "@storybook/experimental-nextjs-vite": "^8.6.4", 27 | "@storybook/react": "^8.6.4", 28 | "storybook": "^8.6.4" 29 | }, 30 | "devDependencies": { 31 | "@eslint/js": "^9.21.0", 32 | "@local/eslint-config": "workspace:*", 33 | "@local/prettier-config": "workspace:*", 34 | "@local/tailwind-config": "workspace:*", 35 | "@local/tsconfig": "workspace:*", 36 | "@types/node": "^20.17.23", 37 | "eslint": "^9.21.0", 38 | "prettier": "^3.5.3", 39 | "react": "catalog:next15", 40 | "react-dom": "catalog:next15", 41 | "tailwindcss": "^3.4.17", 42 | "typescript": "^5.8.2" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /apps/docs/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/docs/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from 'tailwindcss' 6 | 7 | import baseConfig from '@local/tailwind-config' 8 | 9 | export default { 10 | content: [ 11 | '../../apps/*/src/**/*.{ts,tsx}', 12 | '../../packages/*/src/**/*.{ts,tsx}', 13 | ], 14 | presets: [baseConfig], 15 | } satisfies Config 16 | -------------------------------------------------------------------------------- /apps/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "ES2022"], 6 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 7 | }, 8 | "exclude": ["node_modules"], 9 | "extends": "@local/tsconfig/base.json", 10 | "include": [".", ".storybook/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /apps/next/.env.development: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | -------------------------------------------------------------------------------- /apps/next/.env.development.local: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | 7 | # .env.development 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI="mongodb://127.0.0.1/payload-mono" 10 | PAYLOAD_PRIVATE_DATABASE_ENGINE='mongo' 11 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 12 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 13 | 14 | # NEXTJS 15 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 16 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 17 | -------------------------------------------------------------------------------- /apps/next/.env.keys: -------------------------------------------------------------------------------- 1 | ../../.env.keys -------------------------------------------------------------------------------- /apps/next/.env.prod: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_PROD="03e6947395db98e04b2c1ac57b82744425224746d70ffe8b61d6920cff4e0ac724" 6 | 7 | # .env.prod 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI=mongodb://127.0.0.1:27017/cms 10 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 11 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 12 | 13 | # NEXTJS 14 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 15 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 16 | -------------------------------------------------------------------------------- /apps/next/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .pnpm-store 8 | .yarn/install-state.gz 9 | 10 | /.idea/* 11 | !/.idea/runConfigurations 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | .env 40 | 41 | /media 42 | -------------------------------------------------------------------------------- /apps/next/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/next/.prettierignore: -------------------------------------------------------------------------------- 1 | src/payload.types.ts 2 | src/app/(payload)/admin/importMap.js 3 | -------------------------------------------------------------------------------- /apps/next/README.md: -------------------------------------------------------------------------------- 1 | # Payload Blank Template 2 | 3 | A blank template for [Payload](https://github.com/payloadcms/payload) to help you get up and running quickly. This repo may have been created by running `npx create-payload-app@latest` and selecting the "blank" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank). 4 | 5 | See the official [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) for details on how to use Payload in a variety of different ways. 6 | 7 | ## Development 8 | 9 | To spin up the project locally, follow these steps: 10 | 11 | 1. First clone the repo 12 | 1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env` 13 | 1. Next `yarn && yarn dev` (or `docker-compose up`, see [Docker](#docker)) 14 | 1. Now `open http://localhost:3000/admin` to access the admin panel 15 | 1. Create your first admin user using the form on the page 16 | 17 | That's it! Changes made in `./src` will be reflected in your app. 18 | 19 | ### Docker 20 | 21 | Alternatively, you can use [Docker](https://www.docker.com) to spin up this project locally. To do so, follow these steps: 22 | 23 | 1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root 24 | 1. Next run `docker-compose up` 25 | 1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user 26 | 27 | That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams. 28 | 29 | ## Production 30 | 31 | To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps: 32 | 33 | 1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle. 34 | 1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory. 35 | 36 | ### Deployment 37 | 38 | The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details. 39 | 40 | ## Questions 41 | 42 | If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). 43 | -------------------------------------------------------------------------------- /apps/next/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mongo: 5 | image: mongo:latest 6 | ports: 7 | - '27017:27017' 8 | command: 9 | - --storageEngine=wiredTiger 10 | volumes: 11 | - data:/data/db 12 | logging: 13 | driver: none 14 | 15 | volumes: 16 | data: 17 | node_modules: 18 | -------------------------------------------------------------------------------- /apps/next/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import base from '@local/eslint-config/next' 2 | 3 | /** @typedef {import('eslint').Linter.Config} Config */ 4 | 5 | /** @type {Config[]} */ 6 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 7 | const baseConfig = base 8 | 9 | const config = [ 10 | { 11 | ignores: ['src/app/(payload)/*', 'src/payload.types.ts', 'next-env.d.ts'], 12 | name: 'Ignore Payload files', 13 | }, 14 | ...baseConfig, 15 | ] 16 | 17 | export default config 18 | -------------------------------------------------------------------------------- /apps/next/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next' 2 | import { withPayload } from '@payloadcms/next/withPayload' 3 | 4 | const nextConfig: NextConfig = { 5 | eslint: { 6 | ignoreDuringBuilds: true, 7 | }, 8 | 9 | reactStrictMode: true, 10 | 11 | typescript: { 12 | ignoreBuildErrors: true, 13 | }, 14 | } 15 | 16 | export default withPayload(nextConfig) 17 | -------------------------------------------------------------------------------- /apps/next/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "next", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation\" next build", 8 | "ci": "payload migrate && pnpm build --filter payload", 9 | "clean": "git clean -xdf .next .turbo node_modules", 10 | "dev": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation --inspect\" next dev --port 3001", 11 | "dev:clean": "rm -rf .next && cross-env NODE_OPTIONS=\"--no-deprecation\" next dev", 12 | "dev:clean:turbo": "rm -rf .next && cross-env NODE_OPTIONS=\"--no-deprecation\" next dev --turbo", 13 | "dev:turbo": "cross-env NODE_OPTIONS=\"--no-deprecation\" next dev --turbo", 14 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 15 | "generate:schema": "payload-graphql generate:schema", 16 | "generate:types": "payload generate:types", 17 | "lint": "eslint .", 18 | "payload": "cross-env NODE_OPTIONS=\"--no-deprecation\" payload", 19 | "reinstall": "pnpm clean && pnpm i", 20 | "standalone-script": "node ./src/scripts/standalone-script.js", 21 | "start": "cross-env NODE_OPTIONS=\"--no-deprecation\" next start", 22 | "typecheck": "tsc --noEmit" 23 | }, 24 | "prettier": "@local/prettier-config", 25 | "dependencies": { 26 | "@local/env": "workspace:*", 27 | "@local/payload": "workspace:*", 28 | "@local/ui": "workspace:*", 29 | "@payloadcms/live-preview-react": "^3.27.0", 30 | "@payloadcms/next": "^3.27.0", 31 | "babel-plugin-react-compiler": "^0.0.0", 32 | "cross-env": "^7.0.3", 33 | "next": "catalog:next15", 34 | "payload": "^3.27.0", 35 | "react": "catalog:next15", 36 | "react-dom": "catalog:next15", 37 | "sharp": "^0.33.5" 38 | }, 39 | "devDependencies": { 40 | "@dotenvx/dotenvx": "^1.38.4", 41 | "@local/eslint-config": "workspace:*", 42 | "@local/prettier-config": "workspace:*", 43 | "@local/tailwind-config": "workspace:*", 44 | "@local/tsconfig": "workspace:*", 45 | "@local/types": "workspace:*", 46 | "@types/node": "^20.17.23", 47 | "@types/react": "catalog:next15", 48 | "@types/react-dom": "catalog:next15", 49 | "eslint": "^9.21.0", 50 | "prettier": "^3.5.3", 51 | "tailwindcss": "^3.4.17", 52 | "typescript": "^5.8.2" 53 | }, 54 | "engines": { 55 | "node": "^18.20.2 || >=20.9.0" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /apps/next/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/next/run-docker-compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if command -v docker-compose >/dev/null 2>&1; then 4 | docker-compose up -d 5 | elif command -v docker >/dev/null 2>&1 && docker compose >/dev/null 2>&1; then 6 | docker compose up -d 7 | else 8 | echo "Neither docker-compose nor docker compose is installed." 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/(marketing)/[[...slug]]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC } from 'react' 3 | import { cache } from 'react' 4 | import { notFound } from 'next/navigation' 5 | import { getPayload } from 'payload' 6 | 7 | import config from '@local/payload/payload-config' 8 | 9 | import { Preview } from './preview' 10 | 11 | type ContentPageProps = { 12 | params: Promise<{ slug?: string[] }> 13 | searchParams: Promise<{ 14 | isLivePreview?: string 15 | }> 16 | } 17 | 18 | const getPage = cache(async (slug?: string[], isLivePreview?: boolean) => { 19 | const payload = await getPayload({ config }) 20 | const pathname = `/${slug?.join('/') ?? ''}` 21 | const response = await payload.find({ 22 | collection: 'pages', 23 | draft: isLivePreview === true, 24 | where: { pathname: { equals: pathname } }, 25 | }) 26 | 27 | return response.docs[0] 28 | }) 29 | 30 | const getPages = cache(async () => { 31 | const payload = await getPayload({ config }) 32 | const response = await payload.find({ collection: 'pages' }) 33 | 34 | return response.docs 35 | }) 36 | 37 | export const generateMetadata = async ({ 38 | params, 39 | searchParams, 40 | }: ContentPageProps): Promise => { 41 | const { slug } = await params 42 | const { isLivePreview } = await searchParams 43 | const page = await getPage(slug, Boolean(isLivePreview)) 44 | 45 | if (!page?.meta) { 46 | return {} 47 | } 48 | 49 | return page.meta 50 | } 51 | 52 | export const generateStaticParams = async (): Promise< 53 | Awaited>[] 54 | > => { 55 | const pages = await getPages() 56 | 57 | return pages 58 | .filter(page => page.slug) 59 | .map(page => ({ 60 | slug: page.slug === '' ? [''] : [page.slug ?? ''], 61 | })) 62 | } 63 | 64 | const ContentPage: FC = async ({ params, searchParams }) => { 65 | const { slug } = await params 66 | const { isLivePreview } = await searchParams 67 | const page = await getPage(slug, Boolean(isLivePreview)) 68 | 69 | if (!page) { 70 | return notFound() 71 | } 72 | 73 | const { title } = page 74 | 75 | return ( 76 | <> 77 | 78 |

{title}

79 | 80 | ) 81 | } 82 | 83 | export default ContentPage 84 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/(marketing)/[[...slug]]/preview.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { FC } from 'react' 4 | import { useRouter } from 'next/navigation' 5 | import { RefreshRouteOnSave } from '@payloadcms/live-preview-react' 6 | 7 | import { env } from '@local/env/payload' 8 | 9 | export const Preview: FC = () => { 10 | const router = useRouter() 11 | 12 | return ( 13 | { 15 | router.refresh() 16 | }} 17 | serverURL={env.NEXT_PUBLIC_PAYLOAD_URL} 18 | /> 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/(news)/articles/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC } from 'react' 3 | import { cache } from 'react' 4 | import { notFound } from 'next/navigation' 5 | import { getPayload } from 'payload' 6 | 7 | import config from '@local/payload/payload-config' 8 | 9 | import { Preview } from './preview' 10 | 11 | type ArticlePageProps = { 12 | params: Promise<{ slug: string }> 13 | searchParams: Promise<{ 14 | isLivePreview?: string 15 | }> 16 | } 17 | 18 | const getArticle = cache(async (slug: string, isLivePreview?: boolean) => { 19 | const payload = await getPayload({ config }) 20 | const response = await payload.find({ 21 | collection: 'articles', 22 | draft: isLivePreview === true, 23 | where: { slug: { equals: slug } }, 24 | }) 25 | 26 | return response.docs[0] 27 | }) 28 | 29 | const getArticles = cache(async () => { 30 | const payload = await getPayload({ config }) 31 | const response = await payload.find({ collection: 'articles' }) 32 | 33 | return response.docs 34 | }) 35 | 36 | export const generateMetadata = async ({ 37 | params, 38 | searchParams, 39 | }: ArticlePageProps): Promise => { 40 | const { slug } = await params 41 | const { isLivePreview } = await searchParams 42 | const article = await getArticle(slug, Boolean(isLivePreview)) 43 | 44 | if (!article?.meta) { 45 | return {} 46 | } 47 | 48 | return article.meta 49 | } 50 | 51 | export const generateStaticParams = async (): Promise< 52 | Awaited>[] 53 | > => { 54 | const articles = await getArticles() 55 | 56 | return articles.reduce>[]>( 57 | (acc, { slug }) => { 58 | if (!slug) { 59 | return acc 60 | } 61 | 62 | return [...acc, { slug }] 63 | }, 64 | [], 65 | ) 66 | } 67 | 68 | const ArticlePage: FC = async ({ params, searchParams }) => { 69 | const { slug } = await params 70 | const { isLivePreview } = await searchParams 71 | const article = await getArticle(slug, Boolean(isLivePreview)) 72 | 73 | if (!article) { 74 | return notFound() 75 | } 76 | 77 | const { title } = article 78 | 79 | return ( 80 | <> 81 | 82 |

{title}

83 | 84 | ) 85 | } 86 | 87 | export default ArticlePage 88 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/(news)/articles/[slug]/preview.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { FC } from 'react' 4 | import { useRouter } from 'next/navigation' 5 | import { RefreshRouteOnSave } from '@payloadcms/live-preview-react' 6 | 7 | import { env } from '@local/env/payload' 8 | 9 | export const Preview: FC = () => { 10 | const router = useRouter() 11 | 12 | return ( 13 | { 15 | router.refresh() 16 | }} 17 | serverURL={env.NEXT_PUBLIC_PAYLOAD_URL} 18 | /> 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC, PropsWithChildren, ReactNode } from 'react' 3 | 4 | import '@local/ui/styles.css' 5 | import './globals.css' 6 | 7 | export const metadata: Metadata = { 8 | description: 9 | 'Boilerplate for creating a new Payload project using Next.js and TypeScript.', 10 | title: { 11 | default: '', 12 | template: '%s | Payload Monorepo Template', 13 | }, 14 | } 15 | 16 | export type RootLayoutProps = PropsWithChildren<{ 17 | footer?: ReactNode 18 | head?: ReactNode 19 | header?: ReactNode 20 | }> 21 | 22 | const Root: FC = ({ children, footer, head, header }) => { 23 | return ( 24 | 25 | {head} 26 | 27 | {header} 28 | {children} 29 | {footer} 30 | 31 | 32 | ) 33 | } 34 | 35 | export default Root 36 | -------------------------------------------------------------------------------- /apps/next/src/app/(app)/not-found.tsx: -------------------------------------------------------------------------------- 1 | const NotFound = () => ( 2 |
3 |
4 |

404

5 |

6 | Page not found 7 |

8 |

9 | Sorry, we couldn’t find the page you’re looking for. 10 |

11 | 19 |
20 |
21 | ) 22 | 23 | export default NotFound 24 | -------------------------------------------------------------------------------- /apps/next/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/next/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/next/src/payload.config.ts: -------------------------------------------------------------------------------- 1 | import { configurePayload } from '@local/payload/configurePayload' 2 | 3 | export default configurePayload() 4 | -------------------------------------------------------------------------------- /apps/next/src/payload.types.ts: -------------------------------------------------------------------------------- 1 | export * from '@local/payload/payload-types' 2 | -------------------------------------------------------------------------------- /apps/next/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from 'tailwindcss' 6 | 7 | import baseConfig from '@local/tailwind-config' 8 | 9 | export default { 10 | content: [ 11 | ...baseConfig.content, 12 | '../../packages/*/src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', 13 | ], 14 | presets: [baseConfig], 15 | } satisfies Config 16 | -------------------------------------------------------------------------------- /apps/next/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["es2022", "dom", "dom.iterable"], 6 | "module": "esnext", 7 | "paths": { 8 | "@payload-config": ["./src/payload.config.ts"], 9 | "@payload-types": ["./src/payload.types.ts"], 10 | "~/*": ["./src/*"] 11 | }, 12 | "plugins": [ 13 | { 14 | "name": "next" 15 | } 16 | ], 17 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 18 | }, 19 | "exclude": [ 20 | "next-env.d.ts", 21 | "node_modules", 22 | "src/payload.types.ts", 23 | "src/app/(payload)", 24 | ".next" 25 | ], 26 | "extends": "@local/tsconfig/base.json", 27 | "include": [".", ".next/types/**/*.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/payload/.env.development: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | -------------------------------------------------------------------------------- /apps/payload/.env.development.local: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | 7 | # .env.development 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI="mongodb://127.0.0.1/payload-mono" 10 | PAYLOAD_PRIVATE_DATABASE_ENGINE='mongo' 11 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 12 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 13 | 14 | # NEXTJS 15 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 16 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 17 | -------------------------------------------------------------------------------- /apps/payload/.env.keys: -------------------------------------------------------------------------------- 1 | ../../.env.keys -------------------------------------------------------------------------------- /apps/payload/.env.prod: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_PROD="03e6947395db98e04b2c1ac57b82744425224746d70ffe8b61d6920cff4e0ac724" 6 | 7 | # .env.prod 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI=mongodb://127.0.0.1:27017/cms 10 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 11 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 12 | 13 | # NEXTJS 14 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 15 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 16 | -------------------------------------------------------------------------------- /apps/payload/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | .pnpm-store 8 | .yarn/install-state.gz 9 | 10 | /.idea/* 11 | !/.idea/runConfigurations 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # vercel 33 | .vercel 34 | 35 | # typescript 36 | *.tsbuildinfo 37 | next-env.d.ts 38 | 39 | .env 40 | 41 | /media 42 | -------------------------------------------------------------------------------- /apps/payload/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/payload/.prettierignore: -------------------------------------------------------------------------------- 1 | src/payload.types.ts 2 | src/app/(payload)/admin/importMap.js 3 | -------------------------------------------------------------------------------- /apps/payload/README.md: -------------------------------------------------------------------------------- 1 | # Payload Blank Template 2 | 3 | A blank template for [Payload](https://github.com/payloadcms/payload) to help you get up and running quickly. This repo may have been created by running `npx create-payload-app@latest` and selecting the "blank" template or by cloning this template on [Payload Cloud](https://payloadcms.com/new/clone/blank). 4 | 5 | See the official [Examples Directory](https://github.com/payloadcms/payload/tree/main/examples) for details on how to use Payload in a variety of different ways. 6 | 7 | ## Development 8 | 9 | To spin up the project locally, follow these steps: 10 | 11 | 1. First clone the repo 12 | 1. Then `cd YOUR_PROJECT_REPO && cp .env.example .env` 13 | 1. Next `yarn && yarn dev` (or `docker-compose up`, see [Docker](#docker)) 14 | 1. Now `open http://localhost:3000/admin` to access the admin panel 15 | 1. Create your first admin user using the form on the page 16 | 17 | That's it! Changes made in `./src` will be reflected in your app. 18 | 19 | ### Docker 20 | 21 | Alternatively, you can use [Docker](https://www.docker.com) to spin up this project locally. To do so, follow these steps: 22 | 23 | 1. Follow [steps 1 and 2 from above](#development), the docker-compose file will automatically use the `.env` file in your project root 24 | 1. Next run `docker-compose up` 25 | 1. Follow [steps 4 and 5 from above](#development) to login and create your first admin user 26 | 27 | That's it! The Docker instance will help you get up and running quickly while also standardizing the development environment across your teams. 28 | 29 | ## Production 30 | 31 | To run Payload in production, you need to build and serve the Admin panel. To do so, follow these steps: 32 | 33 | 1. First invoke the `payload build` script by running `yarn build` or `npm run build` in your project root. This creates a `./build` directory with a production-ready admin bundle. 34 | 1. Then run `yarn serve` or `npm run serve` to run Node in production and serve Payload from the `./build` directory. 35 | 36 | ### Deployment 37 | 38 | The easiest way to deploy your project is to use [Payload Cloud](https://payloadcms.com/new/import), a one-click hosting solution to deploy production-ready instances of your Payload apps directly from your GitHub repo. You can also deploy your app manually, check out the [deployment documentation](https://payloadcms.com/docs/production/deployment) for full details. 39 | 40 | ## Questions 41 | 42 | If you have any issues or questions, reach out to us on [Discord](https://discord.com/invite/payload) or start a [GitHub discussion](https://github.com/payloadcms/payload/discussions). 43 | -------------------------------------------------------------------------------- /apps/payload/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | mongo: 5 | image: mongo:latest 6 | ports: 7 | - '27017:27017' 8 | command: 9 | - --storageEngine=wiredTiger 10 | volumes: 11 | - data:/data/db 12 | logging: 13 | driver: none 14 | 15 | volumes: 16 | data: 17 | node_modules: 18 | -------------------------------------------------------------------------------- /apps/payload/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import base from '@local/eslint-config/next' 2 | 3 | /** @typedef {import('eslint').Linter.Config} Config */ 4 | 5 | /** @type {Config[]} */ 6 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 7 | const baseConfig = base 8 | 9 | const config = [ 10 | { 11 | ignores: ['src/app/(payload)/*', 'src/payload.types.ts', 'next-env.d.ts'], 12 | name: 'Ignore Payload files', 13 | }, 14 | ...baseConfig, 15 | ] 16 | 17 | export default config 18 | -------------------------------------------------------------------------------- /apps/payload/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from 'next' 2 | import { withPayload } from '@payloadcms/next/withPayload' 3 | 4 | const nextConfig: NextConfig = { 5 | eslint: { 6 | ignoreDuringBuilds: true, 7 | }, 8 | 9 | reactStrictMode: true, 10 | 11 | typescript: { 12 | ignoreBuildErrors: true, 13 | }, 14 | } 15 | 16 | export default withPayload(nextConfig) 17 | -------------------------------------------------------------------------------- /apps/payload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "payload", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "scripts": { 7 | "build": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation\" next build", 8 | "ci": "payload migrate && pnpm build --filter payload", 9 | "clean": "git clean -xdf .next .turbo node_modules", 10 | "dev": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation --inspect\" next dev", 11 | "dev:clean": "rm -rf .next && cross-env NODE_OPTIONS=\"--no-deprecation\" next dev", 12 | "dev:clean:turbo": "rm -rf .next && cross-env NODE_OPTIONS=\"--no-deprecation\" next dev --turbo", 13 | "dev:turbo": "cross-env NODE_OPTIONS=\"--no-deprecation\" next dev --turbo", 14 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 15 | "generate:schema": "payload-graphql generate:schema", 16 | "generate:types": "payload generate:types", 17 | "lint": "eslint .", 18 | "payload": "cross-env NODE_OPTIONS=\"--no-deprecation\" payload", 19 | "reinstall": "pnpm clean && pnpm i", 20 | "standalone-script": "node ./src/scripts/standalone-script.js", 21 | "start": "cross-env NODE_OPTIONS=\"--no-deprecation\" next start", 22 | "typecheck": "tsc --noEmit" 23 | }, 24 | "prettier": "@local/prettier-config", 25 | "dependencies": { 26 | "@local/env": "workspace:*", 27 | "@local/payload": "workspace:*", 28 | "@local/ui": "workspace:*", 29 | "@payloadcms/db-mongodb": "^3.27.0", 30 | "@payloadcms/db-sqlite": "^3.27.0", 31 | "@payloadcms/graphql": "^3.27.0", 32 | "@payloadcms/live-preview-react": "^3.27.0", 33 | "@payloadcms/next": "^3.27.0", 34 | "@payloadcms/plugin-nested-docs": "^3.27.0", 35 | "@payloadcms/plugin-redirects": "^3.27.0", 36 | "@payloadcms/plugin-seo": "^3.27.0", 37 | "@payloadcms/richtext-lexical": "^3.27.0", 38 | "babel-plugin-react-compiler": "^0.0.0", 39 | "cross-env": "^7.0.3", 40 | "graphql": "^16.10.0", 41 | "next": "catalog:next15", 42 | "payload": "^3.27.0", 43 | "react": "catalog:next15", 44 | "react-dom": "catalog:next15", 45 | "sharp": "^0.33.5" 46 | }, 47 | "devDependencies": { 48 | "@dotenvx/dotenvx": "^1.38.4", 49 | "@local/eslint-config": "workspace:*", 50 | "@local/prettier-config": "workspace:*", 51 | "@local/tailwind-config": "workspace:*", 52 | "@local/tsconfig": "workspace:*", 53 | "@local/types": "workspace:*", 54 | "@types/node": "^20.17.23", 55 | "@types/react": "catalog:next15", 56 | "@types/react-dom": "catalog:next15", 57 | "eslint": "^9.21.0", 58 | "prettier": "^3.5.3", 59 | "tailwindcss": "^3.4.17", 60 | "typescript": "^5.8.2" 61 | }, 62 | "engines": { 63 | "node": "^18.20.2 || >=20.9.0" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /apps/payload/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/payload/run-docker-compose.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | if command -v docker-compose >/dev/null 2>&1; then 4 | docker-compose up -d 5 | elif command -v docker >/dev/null 2>&1 && docker compose >/dev/null 2>&1; then 6 | docker compose up -d 7 | else 8 | echo "Neither docker-compose nor docker compose is installed." 9 | exit 1 10 | fi 11 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/(marketing)/[[...slug]]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC } from 'react' 3 | import { cache } from 'react' 4 | import { notFound } from 'next/navigation' 5 | import { getPayload } from 'payload' 6 | 7 | import config from '@local/payload/payload-config' 8 | 9 | import { Preview } from './preview' 10 | 11 | type ContentPageProps = { 12 | params: Promise<{ slug?: string[] }> 13 | searchParams: Promise<{ 14 | isLivePreview?: string 15 | }> 16 | } 17 | 18 | const getPage = cache(async (slug?: string[], isLivePreview?: boolean) => { 19 | const payload = await getPayload({ config }) 20 | const pathname = `/${slug?.join('/') ?? ''}` 21 | const response = await payload.find({ 22 | collection: 'pages', 23 | draft: isLivePreview === true, 24 | where: { pathname: { equals: pathname } }, 25 | }) 26 | 27 | return response.docs[0] 28 | }) 29 | 30 | const getPages = cache(async () => { 31 | const payload = await getPayload({ config }) 32 | const response = await payload.find({ collection: 'pages' }) 33 | 34 | return response.docs 35 | }) 36 | 37 | export const generateMetadata = async ({ 38 | params, 39 | searchParams, 40 | }: ContentPageProps): Promise => { 41 | const { slug } = await params 42 | const { isLivePreview } = await searchParams 43 | const page = await getPage(slug, Boolean(isLivePreview)) 44 | 45 | if (!page?.meta) { 46 | return {} 47 | } 48 | 49 | return page.meta 50 | } 51 | 52 | export const generateStaticParams = async (): Promise< 53 | Awaited>[] 54 | > => { 55 | const pages = await getPages() 56 | 57 | return pages 58 | .filter(page => page.slug) 59 | .map(page => ({ 60 | slug: page.slug === '' ? [''] : [page.slug ?? ''], 61 | })) 62 | } 63 | 64 | const ContentPage: FC = async ({ params, searchParams }) => { 65 | const { slug } = await params 66 | const { isLivePreview } = await searchParams 67 | const page = await getPage(slug, Boolean(isLivePreview)) 68 | 69 | if (!page) { 70 | return notFound() 71 | } 72 | 73 | const { title } = page 74 | 75 | return ( 76 | <> 77 | 78 |

{title}

79 | 80 | ) 81 | } 82 | 83 | export default ContentPage 84 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/(marketing)/[[...slug]]/preview.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { FC } from 'react' 4 | import { useRouter } from 'next/navigation' 5 | import { RefreshRouteOnSave } from '@payloadcms/live-preview-react' 6 | 7 | import { env } from '@local/env/payload' 8 | 9 | export const Preview: FC = () => { 10 | const router = useRouter() 11 | 12 | return ( 13 | { 15 | router.refresh() 16 | }} 17 | serverURL={env.NEXT_PUBLIC_PAYLOAD_URL} 18 | /> 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/(news)/articles/[slug]/page.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC } from 'react' 3 | import { cache } from 'react' 4 | import { notFound } from 'next/navigation' 5 | import { getPayload } from 'payload' 6 | 7 | import config from '@local/payload/payload-config' 8 | 9 | import { Preview } from './preview' 10 | 11 | type ArticlePageProps = { 12 | params: Promise<{ slug: string }> 13 | searchParams: Promise<{ 14 | isLivePreview?: string 15 | }> 16 | } 17 | 18 | const getArticle = cache(async (slug: string, isLivePreview?: boolean) => { 19 | const payload = await getPayload({ config }) 20 | const response = await payload.find({ 21 | collection: 'articles', 22 | draft: isLivePreview === true, 23 | where: { slug: { equals: slug } }, 24 | }) 25 | 26 | return response.docs[0] 27 | }) 28 | 29 | const getArticles = cache(async () => { 30 | const payload = await getPayload({ config }) 31 | const response = await payload.find({ collection: 'articles' }) 32 | 33 | return response.docs 34 | }) 35 | 36 | export const generateMetadata = async ({ 37 | params, 38 | searchParams, 39 | }: ArticlePageProps): Promise => { 40 | const { slug } = await params 41 | const { isLivePreview } = await searchParams 42 | const article = await getArticle(slug, Boolean(isLivePreview)) 43 | 44 | if (!article?.meta) { 45 | return {} 46 | } 47 | 48 | return article.meta 49 | } 50 | 51 | export const generateStaticParams = async (): Promise< 52 | Awaited>[] 53 | > => { 54 | const articles = await getArticles() 55 | 56 | return articles.reduce>[]>( 57 | (acc, { slug }) => { 58 | if (!slug) { 59 | return acc 60 | } 61 | 62 | return [...acc, { slug }] 63 | }, 64 | [], 65 | ) 66 | } 67 | 68 | const ArticlePage: FC = async ({ params, searchParams }) => { 69 | const { slug } = await params 70 | const { isLivePreview } = await searchParams 71 | const article = await getArticle(slug, Boolean(isLivePreview)) 72 | 73 | if (!article) { 74 | return notFound() 75 | } 76 | 77 | const { title } = article 78 | 79 | return ( 80 | <> 81 | 82 |

{title}

83 | 84 | ) 85 | } 86 | 87 | export default ArticlePage 88 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/(news)/articles/[slug]/preview.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { FC } from 'react' 4 | import { useRouter } from 'next/navigation' 5 | import { RefreshRouteOnSave } from '@payloadcms/live-preview-react' 6 | 7 | import { env } from '@local/env/payload' 8 | 9 | export const Preview: FC = () => { 10 | const router = useRouter() 11 | 12 | return ( 13 | { 15 | router.refresh() 16 | }} 17 | serverURL={env.NEXT_PUBLIC_PAYLOAD_URL} 18 | /> 19 | ) 20 | } 21 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from 'next' 2 | import type { FC, PropsWithChildren, ReactNode } from 'react' 3 | 4 | import '@local/ui/styles.css' 5 | import './globals.css' 6 | 7 | export const metadata: Metadata = { 8 | description: 9 | 'Boilerplate for creating a new Payload project using Next.js and TypeScript.', 10 | title: { 11 | default: '', 12 | template: '%s | Payload Monorepo Template', 13 | }, 14 | } 15 | 16 | export type RootLayoutProps = PropsWithChildren<{ 17 | footer?: ReactNode 18 | head?: ReactNode 19 | header?: ReactNode 20 | }> 21 | 22 | const Root: FC = ({ children, footer, head, header }) => { 23 | return ( 24 | 25 | {head} 26 | 27 | {header} 28 | {children} 29 | {footer} 30 | 31 | 32 | ) 33 | } 34 | 35 | export default Root 36 | -------------------------------------------------------------------------------- /apps/payload/src/app/(app)/not-found.tsx: -------------------------------------------------------------------------------- 1 | const NotFound = () => ( 2 |
3 |
4 |

404

5 |

6 | Page not found 7 |

8 |

9 | Sorry, we couldn’t find the page you’re looking for. 10 |

11 | 19 |
20 |
21 | ) 22 | 23 | export default NotFound 24 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/admin/[[...segments]]/not-found.tsx: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | import type { Metadata } from 'next' 3 | import config from '@payload-config' 4 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ 5 | import { generatePageMetadata, NotFoundPage } from '@payloadcms/next/views' 6 | 7 | import { importMap } from '../importMap.js' 8 | 9 | type Args = { 10 | params: { 11 | segments: string[] 12 | } 13 | searchParams: { 14 | [key: string]: string | string[] 15 | } 16 | } 17 | 18 | export const generateMetadata = ({ 19 | params, 20 | searchParams, 21 | }: Args): Promise => 22 | generatePageMetadata({ config, params, searchParams }) 23 | 24 | const NotFound = ({ params, searchParams }: Args) => 25 | NotFoundPage({ config, importMap, params, searchParams }) 26 | 27 | export default NotFound 28 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/admin/[[...segments]]/page.tsx: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | import type { Metadata } from 'next' 3 | import config from '@payload-config' 4 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ 5 | import { generatePageMetadata, RootPage } from '@payloadcms/next/views' 6 | 7 | import { importMap } from '../importMap.js' 8 | 9 | type Args = { 10 | params: { 11 | segments: string[] 12 | } 13 | searchParams: { 14 | [key: string]: string | string[] 15 | } 16 | } 17 | 18 | export const generateMetadata = ({ 19 | params, 20 | searchParams, 21 | }: Args): Promise => 22 | generatePageMetadata({ config, params, searchParams }) 23 | 24 | const Page = ({ params, searchParams }: Args) => 25 | RootPage({ config, importMap, params, searchParams }) 26 | 27 | export default Page 28 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/admin/importMap.js: -------------------------------------------------------------------------------- 1 | import { SlugField as SlugField_54c19aec388d8aada807928cfb32f01c } from '@local/payload/components/SlugField' 2 | import { OverviewComponent as OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' 3 | import { MetaTitleComponent as MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' 4 | import { MetaDescriptionComponent as MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' 5 | import { MetaImageComponent as MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' 6 | import { PreviewComponent as PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' 7 | import { Icon as Icon_5594284c9caa31b482e8d1c210580876 } from '@local/svg/Icon' 8 | import { Logo as Logo_130859914d84f8390328ec91646b3f3d } from '@local/svg/Logo' 9 | 10 | export const importMap = { 11 | "@local/payload/components/SlugField#SlugField": SlugField_54c19aec388d8aada807928cfb32f01c, 12 | "@payloadcms/plugin-seo/client#OverviewComponent": OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860, 13 | "@payloadcms/plugin-seo/client#MetaTitleComponent": MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860, 14 | "@payloadcms/plugin-seo/client#MetaDescriptionComponent": MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860, 15 | "@payloadcms/plugin-seo/client#MetaImageComponent": MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860, 16 | "@payloadcms/plugin-seo/client#PreviewComponent": PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860, 17 | "@local/svg/Icon#Icon": Icon_5594284c9caa31b482e8d1c210580876, 18 | "@local/svg/Logo#Logo": Logo_130859914d84f8390328ec91646b3f3d 19 | } 20 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/api/[...slug]/route.ts: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | /* DO NOT MODIFY it because it could be re-written at any time. */ 3 | import config from '@payload-config' 4 | import { 5 | REST_DELETE, 6 | REST_GET, 7 | REST_OPTIONS, 8 | REST_PATCH, 9 | REST_POST, 10 | } from '@payloadcms/next/routes' 11 | 12 | export const GET = REST_GET(config) 13 | export const POST = REST_POST(config) 14 | export const DELETE = REST_DELETE(config) 15 | export const PATCH = REST_PATCH(config) 16 | export const OPTIONS = REST_OPTIONS(config) 17 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/api/graphql-playground/route.ts: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | /* DO NOT MODIFY it because it could be re-written at any time. */ 3 | import config from '@payload-config' 4 | import { GRAPHQL_PLAYGROUND_GET } from '@payloadcms/next/routes' 5 | 6 | export const GET = GRAPHQL_PLAYGROUND_GET(config) 7 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/api/graphql/route.ts: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | /* DO NOT MODIFY it because it could be re-written at any time. */ 3 | import config from '@payload-config' 4 | import { GRAPHQL_POST } from '@payloadcms/next/routes' 5 | 6 | export const POST = GRAPHQL_POST(config) 7 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/api/revalidate/route.ts: -------------------------------------------------------------------------------- 1 | import type { NextRequest } from 'next/server' 2 | import { revalidateTag } from 'next/cache' 3 | import { NextResponse } from 'next/server' 4 | 5 | export async function GET(request: NextRequest): Promise { 6 | const collection = request.nextUrl.searchParams.get('collection') 7 | const slug = request.nextUrl.searchParams.get('slug') 8 | const secret = request.nextUrl.searchParams.get('secret') 9 | 10 | console.log({ 11 | collection, 12 | slug, 13 | secret, 14 | key: process.env.NEXT_PRIVATE_REVALIDATION_KEY, 15 | }) 16 | 17 | if ( 18 | !secret || 19 | secret !== process.env.NEXT_PRIVATE_REVALIDATION_KEY || 20 | typeof collection !== 'string' || 21 | typeof slug !== 'string' 22 | ) { 23 | return new NextResponse('Invalid request', { status: 400 }) 24 | } 25 | 26 | if (typeof collection === 'string' && typeof slug === 'string') { 27 | revalidateTag(`${collection}_${slug}`) 28 | 29 | return NextResponse.json({ now: Date.now(), revalidated: true }) 30 | } 31 | 32 | return NextResponse.json({ now: Date.now(), revalidated: false }) 33 | } 34 | -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/custom.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/payload/src/app/(payload)/custom.scss -------------------------------------------------------------------------------- /apps/payload/src/app/(payload)/layout.tsx: -------------------------------------------------------------------------------- 1 | /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */ 2 | /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */ 3 | import config from '@payload-config' 4 | 5 | import '@payloadcms/next/css' 6 | 7 | import type { ServerFunctionClient } from 'payload' 8 | import React from 'react' 9 | import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts' 10 | 11 | import { importMap } from './admin/importMap.js' 12 | 13 | import './custom.scss' 14 | 15 | type Args = { 16 | children: React.ReactNode 17 | } 18 | 19 | const serverFunction: ServerFunctionClient = async function (args) { 20 | 'use server' 21 | return handleServerFunctions({ 22 | ...args, 23 | config, 24 | importMap, 25 | }) 26 | } 27 | 28 | const Layout = ({ children }: Args) => ( 29 | 34 | {children} 35 | 36 | ) 37 | 38 | export default Layout 39 | -------------------------------------------------------------------------------- /apps/payload/src/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/payload/src/app/favicon.ico -------------------------------------------------------------------------------- /apps/payload/src/payload.config.ts: -------------------------------------------------------------------------------- 1 | import { configurePayload } from '@local/payload/configurePayload' 2 | 3 | export default configurePayload() 4 | -------------------------------------------------------------------------------- /apps/payload/src/payload.types.ts: -------------------------------------------------------------------------------- 1 | export * from '@local/payload/payload-types' 2 | -------------------------------------------------------------------------------- /apps/payload/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from 'tailwindcss' 6 | 7 | import baseConfig from '@local/tailwind-config' 8 | 9 | export default { 10 | content: [ 11 | ...baseConfig.content, 12 | '../../packages/*/src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', 13 | ], 14 | presets: [baseConfig], 15 | } satisfies Config 16 | -------------------------------------------------------------------------------- /apps/payload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["es2022", "dom", "dom.iterable"], 6 | "module": "esnext", 7 | "paths": { 8 | "@payload-config": ["./src/payload.config.ts"], 9 | "@payload-types": ["./src/payload.types.ts"], 10 | "~/*": ["./src/*"] 11 | }, 12 | "plugins": [ 13 | { 14 | "name": "next" 15 | } 16 | ], 17 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 18 | }, 19 | "exclude": [ 20 | "next-env.d.ts", 21 | "node_modules", 22 | "src/payload.types.ts", 23 | "src/app/(payload)", 24 | ".next" 25 | ], 26 | "extends": "@local/tsconfig/base.json", 27 | "include": [".", ".next/types/**/*.ts"] 28 | } 29 | -------------------------------------------------------------------------------- /apps/svelte/.env.development: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | -------------------------------------------------------------------------------- /apps/svelte/.env.development.local: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_DEVELOPMENT="032ea744d99f027faf7fec5faaba4e4a74619c8d007ddd83003b522571f29bccad" 6 | 7 | # .env.development 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI="mongodb://127.0.0.1/payload-mono" 10 | PAYLOAD_PRIVATE_DATABASE_ENGINE='mongo' 11 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 12 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 13 | DISABLE_PAYLOAD_HMR=true 14 | 15 | # NEXTJS 16 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 17 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 18 | -------------------------------------------------------------------------------- /apps/svelte/.env.keys: -------------------------------------------------------------------------------- 1 | ../../.env.keys -------------------------------------------------------------------------------- /apps/svelte/.env.prod: -------------------------------------------------------------------------------- 1 | #/-------------------[DOTENV_PUBLIC_KEY]--------------------/ 2 | #/ public-key encryption for .env files / 3 | #/ [how it works](https://dotenvx.com/encryption) / 4 | #/----------------------------------------------------------/ 5 | DOTENV_PUBLIC_KEY_PROD="03e6947395db98e04b2c1ac57b82744425224746d70ffe8b61d6920cff4e0ac724" 6 | 7 | # .env.prod 8 | # PAYLOAD 9 | PAYLOAD_PRIVATE_DATABASE_URI=mongodb://127.0.0.1:27017/cms 10 | PAYLOAD_PRIVATE_SECRET="YOUR_SECRET_HERE" 11 | PAYLOAD_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 12 | DISABLE_PAYLOAD_HMR=true 13 | 14 | # NEXTJS 15 | NEXT_PUBLIC_PAYLOAD_URL=http://localhost:3000 16 | NEXT_PRIVATE_REVALIDATION_KEY=EXAMPLE_REVALIDATION_KEY 17 | -------------------------------------------------------------------------------- /apps/svelte/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | 3 | # Output 4 | .output 5 | .vercel 6 | /.svelte-kit 7 | /build 8 | 9 | # OS 10 | .DS_Store 11 | Thumbs.db 12 | 13 | # Vite 14 | vite.config.js.timestamp-* 15 | vite.config.ts.timestamp-* 16 | -------------------------------------------------------------------------------- /apps/svelte/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /apps/svelte/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true 2 | -------------------------------------------------------------------------------- /apps/svelte/.prettierignore: -------------------------------------------------------------------------------- 1 | # Package Managers 2 | package-lock.json 3 | pnpm-lock.yaml 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /apps/svelte/README.md: -------------------------------------------------------------------------------- 1 | # sv 2 | 3 | Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). 4 | 5 | ## Creating a project 6 | 7 | If you're seeing this, you've probably already done this step. Congrats! 8 | 9 | ```bash 10 | # create a new project in the current directory 11 | npx sv create 12 | 13 | # create a new project in my-app 14 | npx sv create my-app 15 | ``` 16 | 17 | ## Developing 18 | 19 | Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: 20 | 21 | ```bash 22 | npm run dev 23 | 24 | # or start the server and open the app in a new browser tab 25 | npm run dev -- --open 26 | ``` 27 | 28 | ## Building 29 | 30 | To create a production version of your app: 31 | 32 | ```bash 33 | npm run build 34 | ``` 35 | 36 | You can preview the production build with `npm run preview`. 37 | 38 | > To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. 39 | -------------------------------------------------------------------------------- /apps/svelte/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/svelte' 2 | -------------------------------------------------------------------------------- /apps/svelte/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "svelte", 3 | "version": "0.0.1", 4 | "type": "module", 5 | "scripts": { 6 | "build": "vite build", 7 | "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", 8 | "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", 9 | "dev": "dotenvx run --convention=nextjs -- cross-env NODE_OPTIONS=\"--no-deprecation --inspect\" vite dev", 10 | "format": "prettier --write .", 11 | "lint": "eslint .", 12 | "prepare": "svelte-kit sync", 13 | "preview": "vite preview", 14 | "typecheck": "tsc --noEmit" 15 | }, 16 | "prettier": "@local/prettier-config", 17 | "devDependencies": { 18 | "@dotenvx/dotenvx": "^1.38.4", 19 | "@local/env": "workspace:*", 20 | "@local/eslint-config": "workspace:*", 21 | "@local/payload": "workspace:*", 22 | "@local/prettier-config": "workspace:*", 23 | "@local/tailwind-config": "workspace:*", 24 | "@local/tsconfig": "workspace:*", 25 | "@local/types": "workspace:*", 26 | "@local/ui": "workspace:*", 27 | "@sveltejs/adapter-auto": "^3.3.1", 28 | "@sveltejs/adapter-node": "^5.2.12", 29 | "@sveltejs/kit": "^2.18.0", 30 | "@sveltejs/vite-plugin-svelte": "^4.0.4", 31 | "@types/eslint": "^9.6.1", 32 | "autoprefixer": "^10.4.20", 33 | "cross-env": "^7.0.3", 34 | "eslint": "^9.21.0", 35 | "globals": "^15.15.0", 36 | "payload": "^3.27.0", 37 | "prettier": "^3.5.3", 38 | "svelte": "^5.22.6", 39 | "svelte-check": "^4.1.5", 40 | "tailwindcss": "^3.4.17", 41 | "typescript": "^5.8.2", 42 | "vite": "^5.4.14" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /apps/svelte/postcss.config.js: -------------------------------------------------------------------------------- 1 | export default { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /apps/svelte/src/app.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | -------------------------------------------------------------------------------- /apps/svelte/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://svelte.dev/docs/kit/types#app.d.ts 2 | // for information about these interfaces 3 | declare global { 4 | namespace App { 5 | // interface Error {} 6 | // interface Locals {} 7 | // interface PageData {} 8 | // interface PageState {} 9 | // interface Platform {} 10 | } 11 | } 12 | 13 | export {} 14 | -------------------------------------------------------------------------------- /apps/svelte/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 13 |
%sveltekit.body%
14 | 15 | 16 | -------------------------------------------------------------------------------- /apps/svelte/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable unicorn/no-empty-file */ 2 | // place files you want to import through the `$lib` alias in this folder. 3 | -------------------------------------------------------------------------------- /apps/svelte/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | {@render children()} 10 | -------------------------------------------------------------------------------- /apps/svelte/src/routes/[...slug]/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { error } from '@sveltejs/kit' 2 | import { getPayload } from 'payload' 3 | 4 | import { configurePayload } from '@local/payload/configurePayload' 5 | 6 | import type { PageServerLoad } from './$types' 7 | 8 | /** @type {import('some-adapter').Config} */ 9 | export const config = { 10 | runtime: 'node', 11 | } 12 | 13 | export const load: PageServerLoad = async ({ params }) => { 14 | const config = configurePayload() 15 | const payload = await getPayload({ config }) 16 | const data = await payload.find({ 17 | collection: 'pages', 18 | draft: true, 19 | limit: 1, 20 | where: { pathname: { equals: `/${params.slug}` } }, 21 | }) 22 | 23 | const [doc] = data.docs 24 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 25 | if (doc) { 26 | return doc 27 | } 28 | 29 | error(404, 'Not found') 30 | } 31 | -------------------------------------------------------------------------------- /apps/svelte/src/routes/[...slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | {data.meta.title} | Payload Monorepo Template 9 | 10 | 11 |

{data.meta.title}

12 | -------------------------------------------------------------------------------- /apps/svelte/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/apps/svelte/static/favicon.png -------------------------------------------------------------------------------- /apps/svelte/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-auto' 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte' 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | kit: { 7 | // adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list. 8 | // If your environment is not supported, or you settled on a specific environment, switch out the adapter. 9 | // See https://svelte.dev/docs/kit/adapters for more information about adapters. 10 | adapter: adapter(), 11 | }, 12 | 13 | // Consult https://svelte.dev/docs/kit/integrations 14 | // for more information about preprocessors 15 | preprocess: vitePreprocess(), 16 | } 17 | 18 | export default config 19 | -------------------------------------------------------------------------------- /apps/svelte/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | import baseConfig from '@local/tailwind-config' 4 | 5 | export default { 6 | content: [ 7 | ...baseConfig.content, 8 | '../../packages/*/src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}', 9 | ], 10 | presets: [baseConfig], 11 | } satisfies Config 12 | -------------------------------------------------------------------------------- /apps/svelte/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowJs": true, 4 | "checkJs": true, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "moduleResolution": "bundler", 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true 12 | }, 13 | "extends": "./.svelte-kit/tsconfig.json", 14 | "include": ["."] 15 | // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias 16 | // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files 17 | // 18 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 19 | // from the referenced tsconfig.json - TypeScript does not merge them in 20 | } 21 | -------------------------------------------------------------------------------- /apps/svelte/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite' 2 | import { defineConfig } from 'vite' 3 | 4 | export default defineConfig({ 5 | plugins: [sveltekit()], 6 | }) 7 | -------------------------------------------------------------------------------- /docker-compose.override.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mongo: 3 | ports: 4 | - 27017:27017 5 | 6 | volumes: 7 | mongodb-data: 8 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | mongo: 3 | image: mongo:latest 4 | restart: unless-stopped 5 | volumes: 6 | - mongodb-data:/data/db 7 | 8 | volumes: 9 | mongodb-data: 10 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config' 2 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "turbo-payload", 3 | "private": true, 4 | "scripts": { 5 | "build": "turbo build", 6 | "clean": "git clean -xdf node_modules", 7 | "clean:workspaces": "turbo clean", 8 | "dev": "turbo dev", 9 | "format": "turbo format --continue -- --cache --cache-location node_modules/.cache/.prettiercache", 10 | "format:fix": "turbo format --continue -- --write --cache --cache-location node_modules/.cache/.prettiercache", 11 | "preinstall": "npx only-allow pnpm", 12 | "postinstall": "pnpm lint:ws", 13 | "lint": "turbo lint --continue -- --cache --cache-location node_modules/.cache/.eslintcache", 14 | "lint:fix": "turbo lint --continue -- --fix --cache --cache-location node_modules/.cache/.eslintcache", 15 | "lint:ws": "pnpm dlx sherif@latest", 16 | "packages:check": "npm-check-updates", 17 | "packages:sync": "syncpack", 18 | "packages:update": "npm-check-updates -u && pnpm i", 19 | "prepare": "husky", 20 | "storybook": "turbo storybook", 21 | "typecheck": "turbo typecheck --continue" 22 | }, 23 | "prettier": "@local/prettier-config", 24 | "devDependencies": { 25 | "@local/prettier-config": "workspace:*", 26 | "@turbo/gen": "^2.4.4", 27 | "husky": "^9.1.7", 28 | "lint-staged": "^15.4.3", 29 | "npm-check-updates": "^17.1.15", 30 | "prettier": "^3.5.3", 31 | "syncpack": "^12.4.0", 32 | "turbo": "^2.4.4", 33 | "typescript": "^5.8.2" 34 | }, 35 | "packageManager": "pnpm@10.4.1+sha512.c753b6c3ad7afa13af388fa6d808035a008e30ea9993f58c6663e2bc5ff21679aa834db094987129aa4d488b86df57f7b634981b2f827cdcacc698cc0cfb88af", 36 | "engines": { 37 | "node": ">=20.11.1" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/env/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/env/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import base from '@local/eslint-config' 2 | 3 | /** @typedef {import('eslint').Linter.Config} Config */ 4 | 5 | /** @type {Config[]} */ 6 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 7 | const baseConfig = base 8 | 9 | const config = [ 10 | ...baseConfig, 11 | { 12 | name: 'Ignore Process.env', 13 | rules: { 14 | 'no-process-env': 'off', 15 | }, 16 | }, 17 | ] 18 | 19 | export default config 20 | -------------------------------------------------------------------------------- /packages/env/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/env", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | "./*": "./src/*.ts", 9 | "./helpers/*": "./src/helpers/*.ts" 10 | }, 11 | "scripts": { 12 | "clean": "rm -rf .turbo node_modules", 13 | "format": "prettier --check . --ignore-path ../../.gitignore", 14 | "lint": "eslint .", 15 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 16 | }, 17 | "prettier": "@local/prettier-config", 18 | "dependencies": { 19 | "@t3-oss/env-nextjs": "^0.11.1", 20 | "zod": "^3.24.2" 21 | }, 22 | "devDependencies": { 23 | "@local/eslint-config": "workspace:*", 24 | "@local/prettier-config": "workspace:*", 25 | "@local/tsconfig": "workspace:*", 26 | "@types/node": "^20.17.23", 27 | "eslint": "^9.21.0", 28 | "prettier": "^3.5.3", 29 | "typescript": "^5.8.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/env/src/app.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | import { skipValidation } from '@local/env/helpers/skipValidation' 5 | 6 | export const env = createEnv({ 7 | emptyStringAsUndefined: true, 8 | 9 | experimental__runtimeEnv: { 10 | NEXT_PUBLIC_APP_ENV: process.env.NEXT_PUBLIC_APP_ENV, 11 | }, 12 | shared: { 13 | NEXT_PUBLIC_APP_ENV: z 14 | .enum(['local', 'staging', 'production']) 15 | .default('local'), 16 | }, 17 | skipValidation, 18 | }) 19 | -------------------------------------------------------------------------------- /packages/env/src/auth.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | import { skipValidation } from '@local/env/helpers/skipValidation' 5 | 6 | export const env = createEnv({ 7 | emptyStringAsUndefined: true, 8 | 9 | experimental__runtimeEnv: {}, 10 | server: { 11 | AUTH_SECRET: z.string(), 12 | }, 13 | skipValidation, 14 | }) 15 | -------------------------------------------------------------------------------- /packages/env/src/helpers/skipValidation.ts: -------------------------------------------------------------------------------- 1 | export const skipValidation = 2 | Boolean(process.env.CI) || 3 | Boolean(process.env.SKIP_ENV_VALIDATION) || 4 | process.env.npm_lifecycle_event === 'lint' 5 | -------------------------------------------------------------------------------- /packages/env/src/node.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | import { skipValidation } from '@local/env/helpers/skipValidation' 5 | 6 | export const env = createEnv({ 7 | emptyStringAsUndefined: true, 8 | 9 | experimental__runtimeEnv: { 10 | NODE_ENV: process.env.NODE_ENV, 11 | }, 12 | shared: { 13 | NODE_ENV: z 14 | .enum(['development', 'production', 'test']) 15 | .default('development'), 16 | }, 17 | skipValidation, 18 | }) 19 | -------------------------------------------------------------------------------- /packages/env/src/payload.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | import { skipValidation } from '@local/env/helpers/skipValidation' 5 | 6 | export const env = createEnv({ 7 | client: { 8 | NEXT_PUBLIC_PAYLOAD_URL: z.string(), 9 | }, 10 | emptyStringAsUndefined: true, 11 | 12 | experimental__runtimeEnv: { 13 | NEXT_PUBLIC_PAYLOAD_URL: process.env.NEXT_PUBLIC_PAYLOAD_URL, 14 | }, 15 | server: { 16 | PAYLOAD_PRIVATE_DATABASE_ENGINE: z.enum(['mongo']).default('mongo'), 17 | PAYLOAD_PRIVATE_DATABASE_URI: z.string(), 18 | PAYLOAD_PRIVATE_REVALIDATION_KEY: z.string(), 19 | PAYLOAD_PRIVATE_SECRET: z.string(), 20 | }, 21 | skipValidation, 22 | }) 23 | -------------------------------------------------------------------------------- /packages/env/src/seo.ts: -------------------------------------------------------------------------------- 1 | import { createEnv } from '@t3-oss/env-nextjs' 2 | import { z } from 'zod' 3 | 4 | import { skipValidation } from '@local/env/helpers/skipValidation' 5 | 6 | export const env = createEnv({ 7 | client: { 8 | NEXT_PUBLIC_SITE_URL: z.string().url(), 9 | }, 10 | emptyStringAsUndefined: true, 11 | 12 | experimental__runtimeEnv: { 13 | NEXT_PUBLIC_SITE_URL: process.env.NEXT_PUBLIC_SITE_URL, 14 | }, 15 | server: {}, 16 | skipValidation, 17 | }) 18 | -------------------------------------------------------------------------------- /packages/env/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": ["dom", "dom.iterable", "ES2022"], 5 | "paths": { 6 | "@local/env/*": ["src/*.ts"], 7 | "@local/env/helpers/*": ["src/helpers/*.ts"] 8 | }, 9 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 10 | }, 11 | "exclude": ["node_modules"], 12 | "extends": "@local/tsconfig/internal-package.json" 13 | } 14 | -------------------------------------------------------------------------------- /packages/icons/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": ["prettier --write --check --ignore-path ../../.gitignore"], 3 | "*.{js,ts,tsx,cjs,mjs}": [ 4 | "eslint --fix", 5 | "prettier --write --check --ignore-path ../../.gitignore" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /packages/icons/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/react' 2 | -------------------------------------------------------------------------------- /packages/icons/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/icons", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | ".": "./src/index.tsx", 9 | "./*": "./src/*/index.tsx" 10 | }, 11 | "scripts": { 12 | "clean": "rm -rf .turbo node_modules", 13 | "format": "prettier --check . --ignore-path ../../.gitignore", 14 | "lint": "eslint .", 15 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 16 | }, 17 | "prettier": "@local/prettier-config", 18 | "dependencies": { 19 | "lucide-react": "^0.454.0", 20 | "react": "catalog:next15", 21 | "react-dom": "catalog:next15" 22 | }, 23 | "devDependencies": { 24 | "@local/eslint-config": "workspace:*", 25 | "@local/prettier-config": "workspace:*", 26 | "@local/tsconfig": "workspace:*", 27 | "@storybook/react": "^8.6.4", 28 | "@types/node": "^20.17.23", 29 | "@types/react": "catalog:next15", 30 | "eslint": "^9.21.0", 31 | "prettier": "^3.5.3", 32 | "tailwindcss": "^3.4.17", 33 | "typescript": "^5.8.2" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/icons/src/CheckIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import { forwardRef, memo } from 'react' 2 | 3 | export const SvgComponent = () => ( 4 | 11 | 18 | 19 | ) 20 | const ForwardRef = forwardRef(SvgComponent) 21 | const Memo = memo(ForwardRef) 22 | export { Memo as CheckIcon } 23 | -------------------------------------------------------------------------------- /packages/icons/src/InformationCircleIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import type { Ref, SVGProps } from 'react' 2 | import { forwardRef, memo } from 'react' 3 | 4 | const SvgComponent = ( 5 | props: SVGProps, 6 | ref: Ref, 7 | ) => ( 8 | 17 | 24 | 25 | ) 26 | const ForwardRef = forwardRef(SvgComponent) 27 | const Memo = memo(ForwardRef) 28 | export { Memo as InformationCircle } 29 | -------------------------------------------------------------------------------- /packages/icons/src/icons.stories.tsx: -------------------------------------------------------------------------------- 1 | import type { Meta, StoryObj } from '@storybook/react' 2 | 3 | import { ArrayIcon, Icon } from './index' 4 | 5 | const Component: Meta = { 6 | args: {}, 7 | argTypes: { 8 | name: { 9 | control: { type: 'select' }, 10 | options: ArrayIcon, 11 | }, 12 | }, 13 | component: Icon, 14 | title: 'Icons/Icon', 15 | } 16 | 17 | export default Component 18 | 19 | type Story = StoryObj 20 | 21 | export const Default: Story = { 22 | args: { 23 | name: 'MenuIcon', 24 | }, 25 | name: 'Icon', 26 | } 27 | -------------------------------------------------------------------------------- /packages/icons/src/icons.ts: -------------------------------------------------------------------------------- 1 | export * from './CheckIcon' 2 | export * from './InformationCircleIcon' 3 | export { Menu as MenuIcon, Star, StarHalf, X as CloseIcon } from 'lucide-react' 4 | -------------------------------------------------------------------------------- /packages/icons/src/index.tsx: -------------------------------------------------------------------------------- 1 | import type { LucideProps } from 'lucide-react' 2 | import type { FC } from 'react' 3 | import { createElement } from 'react' 4 | 5 | import * as Icons from './icons' 6 | 7 | export const ArrayIcon = Object.keys(Icons) 8 | 9 | export type IconName = keyof typeof Icons 10 | 11 | const NamedIcons: Record = Icons 12 | 13 | export const Icon = (props: { name?: IconName } & LucideProps) => { 14 | const { name, ...rest } = props 15 | 16 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 17 | if (!name || !NamedIcons[name]) { 18 | // eslint-disable-next-line no-console 19 | console.error(`Icon ${JSON.stringify(name)} not found`) 20 | return null 21 | } 22 | 23 | return createElement(NamedIcons[name], rest) 24 | } 25 | -------------------------------------------------------------------------------- /packages/icons/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "ES2022"], 6 | "paths": {}, 7 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 8 | }, 9 | "exclude": ["node_modules"], 10 | "extends": "@local/tsconfig/base.json" 11 | } 12 | -------------------------------------------------------------------------------- /packages/payload/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/payload/.prettierignore: -------------------------------------------------------------------------------- 1 | src/payload.types.ts 2 | -------------------------------------------------------------------------------- /packages/payload/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import base from '@local/eslint-config/react' 2 | 3 | /** @typedef {import('eslint').Linter.Config} Config */ 4 | 5 | /** @type {Config[]} */ 6 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 7 | const baseConfig = base 8 | 9 | const config = [ 10 | { 11 | ignores: ['src/payload.types.ts'], 12 | name: 'Ignore Payload types', 13 | }, 14 | ...baseConfig, 15 | ] 16 | 17 | export default config 18 | -------------------------------------------------------------------------------- /packages/payload/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/payload", 3 | "version": "1.0.0", 4 | "private": true, 5 | "type": "module", 6 | "exports": { 7 | "./*": "./src/*.ts", 8 | "./access/*": "./src/access/*/index.ts", 9 | "./blocks/*": "./src/blocks/*/index.ts", 10 | "./collections/*": "./src/collections/*/index.ts", 11 | "./components/*": "./src/components/*/index.ts", 12 | "./fields/*": "./src/fields/*/index.ts", 13 | "./globals/*": "./src/globals/*/index.ts", 14 | "./helpers/*": "./src/helpers/*/index.ts", 15 | "./hooks/*": "./src/hooks/*/index.ts", 16 | "./plugins/*": "./src/plugins/*/index.ts", 17 | "./payload-types": "./src/payload.types.ts", 18 | "./payload-config": "./src/payload.config.ts" 19 | }, 20 | "scripts": { 21 | "clean": "rm -rf node_modules .pnpm-lock.yaml", 22 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 23 | "generate:types": "PAYLOAD_CONFIG_PATH=src/payload.config.ts payload generate:types", 24 | "lint": "eslint .", 25 | "reinstall": "pnpm clean && pnpm i", 26 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 27 | }, 28 | "prettier": "@local/prettier-config", 29 | "dependencies": { 30 | "@local/env": "workspace:*", 31 | "@local/svg": "workspace:*", 32 | "@local/ui": "workspace:*", 33 | "@local/utils": "workspace:*", 34 | "@payloadcms/db-mongodb": "^3.27.0", 35 | "@payloadcms/graphql": "^3.27.0", 36 | "@payloadcms/live-preview-react": "^3.27.0", 37 | "@payloadcms/plugin-nested-docs": "^3.27.0", 38 | "@payloadcms/plugin-redirects": "^3.27.0", 39 | "@payloadcms/plugin-seo": "^3.27.0", 40 | "@payloadcms/richtext-lexical": "^3.27.0", 41 | "@payloadcms/ui": "^3.27.0", 42 | "lexical": "0.20.0", 43 | "next": "catalog:next15", 44 | "payload": "^3.27.0", 45 | "react": "catalog:next15", 46 | "react-dom": "catalog:next15", 47 | "sharp": "^0.33.5", 48 | "ts-pattern": "^5.6.2" 49 | }, 50 | "devDependencies": { 51 | "@local/eslint-config": "workspace:*", 52 | "@local/prettier-config": "workspace:*", 53 | "@local/tsconfig": "workspace:*", 54 | "@local/types": "workspace:*", 55 | "@types/react": "catalog:next15", 56 | "@types/react-dom": "catalog:next15", 57 | "eslint": "^9.21.0", 58 | "prettier": "^3.5.3", 59 | "typescript": "^5.8.2" 60 | }, 61 | "watch": { 62 | "generate:types": { 63 | "patterns": [ 64 | "src" 65 | ], 66 | "extensions": "ts,tsx", 67 | "quiet": false 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /packages/payload/src/access/AdminAccess/index.ts: -------------------------------------------------------------------------------- 1 | import type { FieldAccess, TypeWithID } from 'payload' 2 | 3 | import type { User } from '@local/payload/payload-types' 4 | 5 | export const AdminAccess: FieldAccess = ({ req: { user } }) => 6 | user?.role === 'admin' 7 | -------------------------------------------------------------------------------- /packages/payload/src/access/EveryoneAccess/index.ts: -------------------------------------------------------------------------------- 1 | import type { FieldAccess, TypeWithID } from 'payload' 2 | 3 | import type { User } from '@local/payload/payload-types' 4 | 5 | export const EveryoneAccess: FieldAccess = () => true 6 | -------------------------------------------------------------------------------- /packages/payload/src/access/LoggedInAccess/index.ts: -------------------------------------------------------------------------------- 1 | import type { FieldAccess, TypeWithID } from 'payload' 2 | 3 | import type { User } from '@local/payload/payload-types' 4 | 5 | export const LoggedInAccess: FieldAccess = ({ 6 | req: { user }, 7 | }) => Boolean(user) 8 | -------------------------------------------------------------------------------- /packages/payload/src/blocks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/packages/payload/src/blocks/.gitkeep -------------------------------------------------------------------------------- /packages/payload/src/collections/Articles/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload' 2 | 3 | import { env } from '@local/env/payload' 4 | import { AdminAccess } from '@local/payload/access/AdminAccess' 5 | import { EveryoneAccess } from '@local/payload/access/EveryoneAccess' 6 | import { hideFromIndexingField } from '@local/payload/fields/hideFromIndexing' 7 | import { publishedDateField } from '@local/payload/fields/publishedDate' 8 | import { slugField } from '@local/payload/fields/slug' 9 | import { titleField } from '@local/payload/fields/title' 10 | import { PopulatePublishDateHook } from '@local/payload/hooks/PopulatePublishDateHook' 11 | import { RevalidatePageHook } from '@local/payload/hooks/RevalidatePageHook' 12 | 13 | export const Articles: CollectionConfig<'articles'> = { 14 | access: { 15 | create: AdminAccess, 16 | delete: AdminAccess, 17 | read: EveryoneAccess, 18 | update: AdminAccess, 19 | }, 20 | admin: { 21 | defaultColumns: ['title', 'slug', 'status', 'updatedAt', 'publishedDate'], 22 | group: 'Content', 23 | livePreview: { 24 | url: ({ data }) => { 25 | if ('slug' in data && typeof data.slug === 'string') { 26 | return `${env.NEXT_PUBLIC_PAYLOAD_URL}/articles/${data.slug}?isLivePreview=true` 27 | } 28 | 29 | return '' 30 | }, 31 | }, 32 | pagination: { 33 | defaultLimit: 25, 34 | }, 35 | useAsTitle: 'title', 36 | }, 37 | fields: [ 38 | { 39 | tabs: [ 40 | { 41 | fields: [...titleField()], 42 | label: 'Content', 43 | }, 44 | { 45 | fields: [...slugField()], 46 | label: 'Path Setup', 47 | }, 48 | ], 49 | type: 'tabs', 50 | }, 51 | ...publishedDateField(), 52 | ...hideFromIndexingField(), 53 | ], 54 | hooks: { 55 | afterChange: [RevalidatePageHook], 56 | beforeChange: [PopulatePublishDateHook], 57 | }, 58 | slug: 'articles', 59 | versions: { 60 | drafts: { 61 | autosave: { 62 | interval: 300, 63 | }, 64 | }, 65 | }, 66 | } 67 | -------------------------------------------------------------------------------- /packages/payload/src/collections/Media/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload' 2 | 3 | import { EveryoneAccess } from '@local/payload/access/EveryoneAccess' 4 | import { LoggedInAccess } from '@local/payload/access/LoggedInAccess' 5 | 6 | export const Media: CollectionConfig<'media'> = { 7 | access: { 8 | create: LoggedInAccess, 9 | delete: LoggedInAccess, 10 | read: EveryoneAccess, 11 | update: LoggedInAccess, 12 | }, 13 | admin: { 14 | group: 'Media', 15 | }, 16 | fields: [ 17 | { 18 | name: 'alt', 19 | required: true, 20 | type: 'text', 21 | }, 22 | ], 23 | slug: 'media', 24 | upload: true, 25 | } 26 | -------------------------------------------------------------------------------- /packages/payload/src/collections/Pages/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload' 2 | import { 3 | createBreadcrumbsField, 4 | createParentField, 5 | } from '@payloadcms/plugin-nested-docs' 6 | 7 | import { env } from '@local/env/payload' 8 | import { AdminAccess } from '@local/payload/access/AdminAccess' 9 | import { EveryoneAccess } from '@local/payload/access/EveryoneAccess' 10 | import { hideFromIndexingField } from '@local/payload/fields/hideFromIndexing' 11 | import { publishedDateField } from '@local/payload/fields/publishedDate' 12 | import { slugField } from '@local/payload/fields/slug' 13 | import { titleField } from '@local/payload/fields/title' 14 | import { PopulatePublishDateHook } from '@local/payload/hooks/PopulatePublishDateHook' 15 | import { RevalidatePageHook } from '@local/payload/hooks/RevalidatePageHook' 16 | 17 | export const Pages: CollectionConfig<'pages'> = { 18 | access: { 19 | create: AdminAccess, 20 | delete: AdminAccess, 21 | read: EveryoneAccess, 22 | update: AdminAccess, 23 | }, 24 | admin: { 25 | defaultColumns: [ 26 | 'title', 27 | 'pathname', 28 | 'status', 29 | 'updatedAt', 30 | 'publishedDate', 31 | ], 32 | group: 'Content', 33 | livePreview: { 34 | url: ({ data }) => { 35 | if ('pathname' in data && typeof data.pathname === 'string') { 36 | return `${env.NEXT_PUBLIC_PAYLOAD_URL}${data.pathname}?isLivePreview=true` 37 | } 38 | 39 | return '' 40 | }, 41 | }, 42 | pagination: { 43 | defaultLimit: 25, 44 | }, 45 | useAsTitle: 'title', 46 | }, 47 | fields: [ 48 | { 49 | tabs: [ 50 | { 51 | fields: [...titleField()], 52 | label: 'Content', 53 | }, 54 | { 55 | fields: [ 56 | ...slugField(), 57 | createParentField('pages'), 58 | createBreadcrumbsField('pages'), 59 | ], 60 | label: 'Path Setup', 61 | }, 62 | ], 63 | type: 'tabs', 64 | }, 65 | ...publishedDateField(), 66 | ...hideFromIndexingField(), 67 | ], 68 | hooks: { 69 | afterChange: [RevalidatePageHook], 70 | beforeChange: [PopulatePublishDateHook], 71 | }, 72 | slug: 'pages', 73 | versions: { 74 | drafts: { 75 | autosave: { 76 | interval: 300, 77 | }, 78 | }, 79 | }, 80 | } 81 | -------------------------------------------------------------------------------- /packages/payload/src/collections/Users/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionConfig } from 'payload' 2 | 3 | import { AdminAccess } from '@local/payload/access/AdminAccess' 4 | import { LoggedInAccess } from '@local/payload/access/LoggedInAccess' 5 | 6 | export const Users: CollectionConfig<'users'> = { 7 | access: { 8 | create: AdminAccess, 9 | read: LoggedInAccess, 10 | update: AdminAccess, 11 | }, 12 | admin: { 13 | group: 'Site Config', 14 | useAsTitle: 'email', 15 | }, 16 | auth: true, 17 | fields: [ 18 | { 19 | defaultValue: 'user', 20 | name: 'role', 21 | options: ['user', 'admin'], 22 | required: true, 23 | type: 'select', 24 | }, 25 | ], 26 | slug: 'users', 27 | } 28 | -------------------------------------------------------------------------------- /packages/payload/src/components/SlugField/index.scss: -------------------------------------------------------------------------------- 1 | .slug-field-component { 2 | .label-wrapper { 3 | display: flex; 4 | justify-content: space-between; 5 | align-items: center; 6 | } 7 | 8 | .lock-button { 9 | margin: 0; 10 | padding-bottom: 0.3125rem; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/payload/src/components/SlugField/index.ts: -------------------------------------------------------------------------------- 1 | export * from './slug-field' 2 | -------------------------------------------------------------------------------- /packages/payload/src/components/SlugField/slug-field.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { TextFieldClientProps } from 'payload' 4 | import type { FC, MouseEvent } from 'react' 5 | import { useCallback, useEffect } from 'react' 6 | import { 7 | Button, 8 | FieldLabel, 9 | TextInput, 10 | useField, 11 | useFormFields, 12 | } from '@payloadcms/ui' 13 | 14 | import { createSlug } from '@local/utils/createSlug' 15 | 16 | import './index.scss' 17 | 18 | type SlugFieldProps = { 19 | checkboxFieldPath: string 20 | fieldToUse: string 21 | } & TextFieldClientProps 22 | 23 | export const SlugField: FC = ({ 24 | checkboxFieldPath: checkboxFieldPathFromProps, 25 | field, 26 | fieldToUse, 27 | path = '', 28 | readOnly: readOnlyFromProps, 29 | }) => { 30 | const { label } = field 31 | 32 | const checkboxFieldPath = path.includes('.') 33 | ? `${path}.${checkboxFieldPathFromProps}` 34 | : checkboxFieldPathFromProps 35 | 36 | const { setValue, value } = useField({ path }) 37 | 38 | const { setValue: setCheckboxValue, value: checkboxValue } = 39 | useField({ 40 | path: checkboxFieldPath, 41 | }) 42 | 43 | const fieldToUseValue = useFormFields(([fields, _dispatch]) => { 44 | if ( 45 | !fieldToUse || 46 | !(fieldToUse in fields) || 47 | typeof fields[fieldToUse] !== 'object' || 48 | !('value' in fields[fieldToUse]) || 49 | typeof fields[fieldToUse].value !== 'string' 50 | ) { 51 | return '' 52 | } 53 | 54 | return fields[fieldToUse].value 55 | }) 56 | 57 | useEffect(() => { 58 | if (checkboxValue) { 59 | if (fieldToUseValue) { 60 | const formattedSlug = createSlug(fieldToUseValue) 61 | 62 | if (value !== formattedSlug) setValue(formattedSlug) 63 | } else if (value !== '') { 64 | setValue('') 65 | } 66 | } 67 | }, [fieldToUseValue, checkboxValue, setValue, value]) 68 | 69 | const handleLock = useCallback( 70 | (evt: MouseEvent) => { 71 | evt.preventDefault() 72 | 73 | setCheckboxValue(!checkboxValue) 74 | }, 75 | [checkboxValue, setCheckboxValue], 76 | ) 77 | 78 | const readOnly = readOnlyFromProps ?? checkboxValue 79 | 80 | return ( 81 |
82 |
83 | 84 | 85 | 88 |
89 | 90 | 97 |
98 | ) 99 | } 100 | -------------------------------------------------------------------------------- /packages/payload/src/configurePayload.ts: -------------------------------------------------------------------------------- 1 | import path from 'node:path' 2 | import { fileURLToPath } from 'node:url' 3 | import type { Config } from 'payload' 4 | import { mongooseAdapter } from '@payloadcms/db-mongodb' 5 | import { redirectsPlugin } from '@payloadcms/plugin-redirects' 6 | import { seoPlugin } from '@payloadcms/plugin-seo' 7 | import { lexicalEditor } from '@payloadcms/richtext-lexical' 8 | import { buildConfig, deepMerge } from 'payload' 9 | 10 | import { env } from '@local/env/payload' 11 | import { Articles } from '@local/payload/collections/Articles' 12 | import { Media } from '@local/payload/collections/Media' 13 | import { Pages } from '@local/payload/collections/Pages' 14 | import { Users } from '@local/payload/collections/Users' 15 | import { Footer } from '@local/payload/globals/Footer' 16 | import { NavigationMenu } from '@local/payload/globals/NavigationMenu' 17 | import { nestedDocsPlusPlugin } from '@local/payload/plugins/nestedDocsPlusPlugin' 18 | import { isObject } from '@local/utils/isObject' 19 | 20 | const filename = fileURLToPath(import.meta.url) 21 | const dirname = path.dirname(filename) 22 | 23 | const baseConfig: Config = { 24 | admin: { 25 | components: { 26 | graphics: { 27 | Icon: '@local/svg/Icon#Icon', 28 | Logo: '@local/svg/Logo#Logo', 29 | }, 30 | }, 31 | meta: { 32 | icons: [ 33 | { 34 | rel: 'shortcut icon', 35 | url: '/favicon.ico', 36 | }, 37 | ], 38 | titleSuffix: ' | Fusionary CMS', 39 | }, 40 | user: Users.slug, 41 | }, 42 | collections: [Pages, Articles, Users, Media], 43 | db: mongooseAdapter({ 44 | url: env.PAYLOAD_PRIVATE_DATABASE_URI, 45 | }), 46 | editor: lexicalEditor(), 47 | globals: [NavigationMenu, Footer], 48 | plugins: [ 49 | nestedDocsPlusPlugin({ 50 | breadcrumbsFieldSlug: 'breadcrumbs', 51 | collections: ['pages'], 52 | generateLabel: (_, doc) => doc.title as string, 53 | generateURL: docs => 54 | docs.reduce((acc, doc) => `${acc}/${doc.slug as string}`, ''), 55 | parentFieldSlug: 'parent', 56 | }), 57 | redirectsPlugin({ 58 | collections: [Pages.slug, Articles.slug], 59 | overrides: { 60 | admin: { 61 | group: 'Site Config', 62 | }, 63 | }, 64 | redirectTypes: ['301', '302', '307', '308'], 65 | }), 66 | seoPlugin({ 67 | collections: ['pages', 'articles'], 68 | fields: ({ defaultFields }) => { 69 | return defaultFields.map(field => { 70 | if ('name' in field && field.name === 'title') { 71 | return { 72 | ...field, 73 | required: true, 74 | } 75 | } 76 | 77 | return field 78 | }) 79 | }, 80 | generateTitle: ({ doc }) => 81 | isObject(doc) && 'title' in doc && typeof doc.title === 'string' 82 | ? doc.title 83 | : '', 84 | tabbedUI: true, 85 | uploadsCollection: 'media', 86 | }), 87 | ], 88 | secret: env.PAYLOAD_PRIVATE_SECRET, 89 | typescript: { 90 | outputFile: path.resolve(dirname, 'payload.types.ts'), 91 | }, 92 | } 93 | 94 | export const configurePayload = (overrides?: Partial) => { 95 | return buildConfig(deepMerge(baseConfig, overrides ?? {})) 96 | } 97 | -------------------------------------------------------------------------------- /packages/payload/src/fields/address/index.ts: -------------------------------------------------------------------------------- 1 | import type { GroupField, TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type AddressFieldOverrides = { 5 | address1Overrides?: Partial 6 | address2Overrides?: Partial 7 | cityOverrides?: Partial 8 | countryOverrides?: Partial 9 | groupOverrides?: Partial> 10 | postalCodeOverrides?: Partial 11 | required?: boolean 12 | stateOverrides?: Partial 13 | } 14 | 15 | type AddressField = (overrides?: AddressFieldOverrides) => GroupField 16 | 17 | export const addressField: AddressField = (overrides = {}) => { 18 | const { 19 | address1Overrides = {}, 20 | address2Overrides = {}, 21 | cityOverrides = {}, 22 | countryOverrides = {}, 23 | groupOverrides = {}, 24 | postalCodeOverrides = {}, 25 | required = false, 26 | stateOverrides = {}, 27 | } = overrides 28 | 29 | return deepMerge( 30 | { 31 | fields: [ 32 | { 33 | fields: [ 34 | deepMerge( 35 | { 36 | label: 'Address 1', 37 | name: 'address1', 38 | required, 39 | type: 'text', 40 | }, 41 | address1Overrides, 42 | ), 43 | deepMerge( 44 | { 45 | label: 'Address 2', 46 | name: 'address2', 47 | type: 'text', 48 | }, 49 | address2Overrides, 50 | ), 51 | ], 52 | type: 'row', 53 | }, 54 | { 55 | fields: [ 56 | deepMerge( 57 | { 58 | label: 'City', 59 | name: 'city', 60 | required, 61 | type: 'text', 62 | }, 63 | cityOverrides, 64 | ), 65 | deepMerge( 66 | { 67 | label: 'State', 68 | name: 'state', 69 | required, 70 | type: 'text', 71 | }, 72 | stateOverrides, 73 | ), 74 | deepMerge( 75 | { 76 | label: 'Postal Code', 77 | name: 'postalCode', 78 | required, 79 | type: 'text', 80 | }, 81 | postalCodeOverrides, 82 | ), 83 | deepMerge( 84 | { 85 | label: 'Country', 86 | name: 'country', 87 | required, 88 | type: 'text', 89 | }, 90 | countryOverrides, 91 | ), 92 | ], 93 | type: 'row', 94 | }, 95 | ], 96 | label: 'Address', 97 | name: 'address', 98 | type: 'group', 99 | }, 100 | groupOverrides, 101 | ) 102 | } 103 | -------------------------------------------------------------------------------- /packages/payload/src/fields/body/index.ts: -------------------------------------------------------------------------------- 1 | import type { TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type BodyFieldOverrides = { 5 | bodyOverrides?: Partial 6 | } 7 | 8 | type BodyField = (overrides?: BodyFieldOverrides) => [TextField] 9 | 10 | export const bodyField: BodyField = overrides => { 11 | const { bodyOverrides = {} } = overrides ?? {} 12 | 13 | const bodyField = deepMerge( 14 | { 15 | label: 'Body', 16 | name: 'body', 17 | type: 'text', 18 | }, 19 | bodyOverrides, 20 | ) 21 | 22 | return [bodyField] 23 | } 24 | -------------------------------------------------------------------------------- /packages/payload/src/fields/eyebrow/index.ts: -------------------------------------------------------------------------------- 1 | import type { TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type EyebrowFieldOverrides = { 5 | eyebrowOverrides?: Partial 6 | } 7 | 8 | type EyebrowField = (overrides?: EyebrowFieldOverrides) => [TextField] 9 | 10 | export const eyebrowField: EyebrowField = (overrides = {}) => { 11 | const { eyebrowOverrides = {} } = overrides 12 | 13 | const eyebrowField = deepMerge( 14 | { 15 | label: 'Eyebrow', 16 | name: 'eyebrow', 17 | type: 'text', 18 | }, 19 | eyebrowOverrides, 20 | ) 21 | 22 | return [eyebrowField] 23 | } 24 | -------------------------------------------------------------------------------- /packages/payload/src/fields/heading/index.ts: -------------------------------------------------------------------------------- 1 | import type { TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type HeadingFieldOverrides = { 5 | headingOverrides?: Partial 6 | } 7 | 8 | type HeadingField = (overrides?: HeadingFieldOverrides) => [TextField] 9 | 10 | export const headingField: HeadingField = (overrides = {}) => { 11 | const { headingOverrides = {} } = overrides 12 | 13 | const headingField = deepMerge( 14 | { 15 | label: 'Heading', 16 | name: 'heading', 17 | required: true, 18 | type: 'text', 19 | }, 20 | headingOverrides, 21 | ) 22 | 23 | return [headingField] 24 | } 25 | -------------------------------------------------------------------------------- /packages/payload/src/fields/hideFromIndexing/index.ts: -------------------------------------------------------------------------------- 1 | import type { CheckboxField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type HideFromIndexingFieldOverrides = { 5 | hideFromIndexingOverrides?: Partial 6 | } 7 | 8 | type HideFromIndexingField = ( 9 | overrides?: HideFromIndexingFieldOverrides, 10 | ) => [CheckboxField] 11 | 12 | export const hideFromIndexingField: HideFromIndexingField = ( 13 | overrides = {}, 14 | ) => { 15 | const { hideFromIndexingOverrides = {} } = overrides 16 | 17 | const hideFromIndexingField = deepMerge( 18 | { 19 | admin: { 20 | position: 'sidebar', 21 | }, 22 | label: 'Hide page from indexing', 23 | name: 'hideFromIndexing', 24 | type: 'checkbox', 25 | }, 26 | hideFromIndexingOverrides, 27 | ) 28 | 29 | return [hideFromIndexingField] 30 | } 31 | -------------------------------------------------------------------------------- /packages/payload/src/fields/image/index.ts: -------------------------------------------------------------------------------- 1 | import type { UploadField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | import type { MediaFieldOverrides } from '@local/payload/fields/media' 5 | import { mediaField } from '@local/payload/fields/media' 6 | 7 | export type ImageFieldOverrides = { 8 | imageOverrides?: { 9 | supportedFormats?: string[] 10 | } 11 | } & MediaFieldOverrides 12 | 13 | type ImageField = (overrides?: ImageFieldOverrides) => [UploadField] 14 | 15 | export const imageField: ImageField = (overrides = {}) => { 16 | const { imageOverrides = {}, ...mediaOverrides } = overrides 17 | const { 18 | supportedFormats = [ 19 | 'image/jpeg', 20 | 'image/png', 21 | 'image/webp', 22 | 'image/svg+xml', 23 | ], 24 | } = imageOverrides 25 | 26 | const imageField = mediaField( 27 | deepMerge( 28 | { 29 | mediaOverrides: { 30 | filterOptions: { 31 | or: supportedFormats.map(format => ({ 32 | mimeType: { 33 | contains: format, 34 | }, 35 | })), 36 | }, 37 | label: 'Image', 38 | name: 'image', 39 | }, 40 | }, 41 | mediaOverrides, 42 | ), 43 | ) 44 | 45 | return [...imageField] 46 | } 47 | -------------------------------------------------------------------------------- /packages/payload/src/fields/link/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CheckboxField, 3 | GroupField, 4 | RadioField, 5 | RelationshipField, 6 | TextField, 7 | } from 'payload' 8 | import { deepMerge } from 'payload' 9 | 10 | export type LinkFieldOverrides = { 11 | groupOverrides?: Partial> 12 | internalLinkOverrides?: Partial 13 | labelOverrides?: Partial 14 | linkTypeOverrides?: Partial 15 | openInNewTabOverrides?: Partial 16 | required?: boolean 17 | urlOverrides?: Partial 18 | } 19 | 20 | type LinkField = (overrides?: LinkFieldOverrides) => GroupField 21 | 22 | export const linkField: LinkField = (overrides = {}) => { 23 | const { 24 | groupOverrides = {}, 25 | internalLinkOverrides = {}, 26 | labelOverrides = {}, 27 | linkTypeOverrides = {}, 28 | openInNewTabOverrides = {}, 29 | required = false, 30 | urlOverrides = {}, 31 | } = overrides 32 | 33 | return deepMerge( 34 | { 35 | fields: [ 36 | deepMerge( 37 | { 38 | label: 'Label', 39 | name: 'label', 40 | required, 41 | type: 'text', 42 | }, 43 | labelOverrides, 44 | ), 45 | deepMerge( 46 | { 47 | admin: { 48 | condition: (_, siblingData) => 49 | required || Boolean(siblingData.label), 50 | }, 51 | label: 'Type', 52 | name: 'linkType', 53 | options: [ 54 | { 55 | label: 'Internal', 56 | value: 'internal', 57 | }, 58 | { 59 | label: 'External', 60 | value: 'external', 61 | }, 62 | ], 63 | required: true, 64 | type: 'radio', 65 | }, 66 | linkTypeOverrides, 67 | ), 68 | deepMerge( 69 | { 70 | admin: { 71 | condition: (_, siblingData) => 72 | (required || Boolean(siblingData.label)) && 73 | siblingData.linkType === 'external', 74 | }, 75 | label: 'URL', 76 | name: 'url', 77 | required: true, 78 | type: 'text', 79 | }, 80 | urlOverrides, 81 | ), 82 | deepMerge( 83 | { 84 | admin: { 85 | condition: (_, siblingData) => 86 | (required || Boolean(siblingData.label)) && 87 | siblingData.linkType === 'internal', 88 | }, 89 | label: 'Internal Reference', 90 | name: 'internalLink', 91 | relationTo: ['pages', 'articles'], 92 | required: true, 93 | type: 'relationship', 94 | }, 95 | internalLinkOverrides, 96 | ), 97 | deepMerge( 98 | { 99 | admin: { 100 | condition: (_, siblingData) => Boolean(siblingData.label), 101 | }, 102 | label: 'Open in new tab', 103 | name: 'openInNewTab', 104 | type: 'checkbox', 105 | }, 106 | openInNewTabOverrides, 107 | ), 108 | ], 109 | label: 'Link', 110 | name: 'link', 111 | type: 'group', 112 | }, 113 | groupOverrides, 114 | ) 115 | } 116 | -------------------------------------------------------------------------------- /packages/payload/src/fields/media/index.ts: -------------------------------------------------------------------------------- 1 | import type { UploadField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type MediaFieldOverrides = { 5 | mediaOverrides?: Partial 6 | } 7 | 8 | type MediaField = (overrides?: MediaFieldOverrides) => [UploadField] 9 | 10 | export const mediaField: MediaField = (overrides = {}) => { 11 | const { mediaOverrides = {} } = overrides 12 | 13 | const mediaField = deepMerge( 14 | { 15 | label: 'Media', 16 | name: 'media', 17 | relationTo: 'media', 18 | type: 'upload', 19 | }, 20 | mediaOverrides, 21 | ) 22 | 23 | return [mediaField] 24 | } 25 | -------------------------------------------------------------------------------- /packages/payload/src/fields/publishedDate/index.ts: -------------------------------------------------------------------------------- 1 | import type { DateField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type PublishedDateFieldOverrides = { 5 | publishedDateOverrides?: Partial 6 | } 7 | 8 | type PublishedDateField = ( 9 | overrides?: PublishedDateFieldOverrides, 10 | ) => [DateField] 11 | 12 | export const publishedDateField: PublishedDateField = (overrides = {}) => { 13 | const { publishedDateOverrides = {} } = overrides 14 | 15 | const publishedDateField = deepMerge( 16 | { 17 | admin: { 18 | position: 'sidebar', 19 | }, 20 | label: 'Published Date', 21 | name: 'publishedDate', 22 | type: 'date', 23 | }, 24 | publishedDateOverrides, 25 | ) 26 | 27 | return [publishedDateField] 28 | } 29 | -------------------------------------------------------------------------------- /packages/payload/src/fields/richText/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | FeatureProviderServer, 3 | LexicalEditorProps, 4 | LexicalRichTextAdapter, 5 | } from '@payloadcms/richtext-lexical' 6 | import type { SerializedEditorState } from 'lexical' 7 | import type { RichTextField } from 'payload' 8 | import { 9 | HTMLConverterFeature, 10 | lexicalEditor, 11 | } from '@payloadcms/richtext-lexical' 12 | import { deepMerge } from 'payload' 13 | 14 | type LexicalRichTextField = RichTextField< 15 | SerializedEditorState, 16 | LexicalRichTextAdapter, 17 | LexicalEditorProps 18 | > 19 | 20 | export type RichTextFieldOverrides = { 21 | richTextOverrides?: Partial 22 | } 23 | 24 | type RichText = ( 25 | overrides?: RichTextFieldOverrides, 26 | additions?: { 27 | features?: FeatureProviderServer[] 28 | }, 29 | ) => [RichTextField] 30 | 31 | export const richTextField: RichText = (overrides = {}, additions = {}) => { 32 | const { richTextOverrides = {} } = overrides 33 | 34 | const richTextField = deepMerge( 35 | { 36 | editor: lexicalEditor({ 37 | features: ({ defaultFeatures }) => [ 38 | ...defaultFeatures, 39 | HTMLConverterFeature({}), 40 | ...(additions.features ?? []), 41 | ], 42 | }), 43 | name: 'richText', 44 | type: 'richText', 45 | }, 46 | richTextOverrides, 47 | ) 48 | 49 | return [richTextField] 50 | } 51 | -------------------------------------------------------------------------------- /packages/payload/src/fields/slug/index.ts: -------------------------------------------------------------------------------- 1 | import type { CheckboxField, TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | import { formatSlugHook } from '@local/payload/hooks/FormatSlugHook' 5 | 6 | export type SlugFieldOverrides = { 7 | checkboxOverrides?: Partial 8 | slugOverrides?: Partial 9 | } 10 | 11 | type Slug = ( 12 | fieldToUse?: string, 13 | overrides?: SlugFieldOverrides, 14 | ) => [TextField, CheckboxField] 15 | 16 | export const slugField: Slug = (fieldToUse = 'title', overrides = {}) => { 17 | const { checkboxOverrides = {}, slugOverrides = {} } = overrides 18 | 19 | const checkBoxField = deepMerge( 20 | { 21 | admin: { 22 | hidden: true, 23 | position: 'sidebar', 24 | }, 25 | defaultValue: true, 26 | name: 'slugLock', 27 | type: 'checkbox', 28 | }, 29 | checkboxOverrides, 30 | ) 31 | 32 | const slugField = deepMerge( 33 | { 34 | admin: { 35 | components: { 36 | Field: { 37 | clientProps: { 38 | checkboxFieldPath: checkBoxField.name, 39 | fieldToUse, 40 | }, 41 | path: '@local/payload/components/SlugField#SlugField', 42 | }, 43 | }, 44 | position: 'sidebar', 45 | }, 46 | hooks: { 47 | // Kept this in for hook or API based updates 48 | beforeValidate: [formatSlugHook(fieldToUse)], 49 | }, 50 | index: true, 51 | label: 'Slug', 52 | name: 'slug', 53 | type: 'text', 54 | }, 55 | slugOverrides, 56 | ) 57 | 58 | return [slugField, checkBoxField] 59 | } 60 | -------------------------------------------------------------------------------- /packages/payload/src/fields/title/index.ts: -------------------------------------------------------------------------------- 1 | import type { TextField } from 'payload' 2 | import { deepMerge } from 'payload' 3 | 4 | export type TextFieldOverrides = { 5 | titleOverrides?: Partial 6 | } 7 | 8 | type TitleField = (overrides?: TextFieldOverrides) => [TextField] 9 | 10 | export const titleField: TitleField = (overrides = {}) => { 11 | const { titleOverrides = {} } = overrides 12 | 13 | const titleField = deepMerge( 14 | { 15 | label: 'Name', 16 | name: 'title', 17 | required: true, 18 | type: 'text', 19 | }, 20 | titleOverrides, 21 | ) 22 | 23 | return [titleField] 24 | } 25 | -------------------------------------------------------------------------------- /packages/payload/src/globals/Footer/index.ts: -------------------------------------------------------------------------------- 1 | import type { GlobalConfig } from 'payload' 2 | 3 | export const Footer: GlobalConfig = { 4 | admin: { 5 | group: 'Site Config', 6 | }, 7 | fields: [], 8 | slug: 'footer', 9 | } 10 | -------------------------------------------------------------------------------- /packages/payload/src/globals/NavigationMenu/index.ts: -------------------------------------------------------------------------------- 1 | import type { GlobalConfig } from 'payload' 2 | 3 | export const NavigationMenu: GlobalConfig = { 4 | admin: { 5 | group: 'Site Config', 6 | }, 7 | fields: [], 8 | slug: 'navigation-menu', 9 | } 10 | -------------------------------------------------------------------------------- /packages/payload/src/helpers/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/packages/payload/src/helpers/.gitkeep -------------------------------------------------------------------------------- /packages/payload/src/hooks/FormatSlugHook/index.ts: -------------------------------------------------------------------------------- 1 | import type { FieldHook } from 'payload' 2 | 3 | import { createSlug } from '@local/utils/createSlug' 4 | 5 | export const formatSlugHook = 6 | (fallback: string): FieldHook => 7 | ({ data, operation, value }) => { 8 | if (typeof value === 'string') { 9 | return createSlug(value) 10 | } 11 | 12 | if ( 13 | data && 14 | (operation === 'create' || !data.slug) && 15 | fallback && 16 | fallback in data && 17 | typeof data[fallback] === 'string' 18 | ) { 19 | return createSlug(data[fallback]) 20 | } 21 | 22 | // eslint-disable-next-line @typescript-eslint/no-unsafe-return 23 | return value 24 | } 25 | -------------------------------------------------------------------------------- /packages/payload/src/hooks/PopulatePublishDateHook/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionBeforeChangeHook } from 'payload' 2 | 3 | export const PopulatePublishDateHook: CollectionBeforeChangeHook = ({ 4 | data, 5 | }) => { 6 | if (data.publishedDate) return 7 | 8 | return { 9 | ...data, 10 | publishedDate: new Date(), 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/payload/src/hooks/RevalidatePageHook/index.ts: -------------------------------------------------------------------------------- 1 | import type { CollectionAfterChangeHook, TypeWithID } from 'payload' 2 | import { revalidatePath } from 'next/cache' 3 | 4 | export const RevalidatePageHook: CollectionAfterChangeHook = ({ 5 | doc, 6 | previousDoc, 7 | req: { payload }, 8 | }) => { 9 | if ( 10 | '_status' in doc && 11 | doc._status === 'published' && 12 | 'slug' in doc && 13 | typeof doc.slug === 'string' 14 | ) { 15 | const path = `/posts/${doc.slug}` 16 | 17 | payload.logger.info(`Revalidating post at path: ${path}`) 18 | 19 | revalidatePath(path) 20 | } 21 | 22 | // If the post was previously published, we need to revalidate the old path 23 | if ( 24 | '_status' in previousDoc && 25 | previousDoc._status === 'published' && 26 | '_status' in doc && 27 | doc._status !== 'published' && 28 | 'slug' in previousDoc && 29 | typeof previousDoc.slug === 'string' 30 | ) { 31 | const oldPath = `/posts/${previousDoc.slug}` 32 | 33 | payload.logger.info(`Revalidating old post at path: ${oldPath}`) 34 | 35 | revalidatePath(oldPath) 36 | } 37 | 38 | return doc 39 | } 40 | -------------------------------------------------------------------------------- /packages/payload/src/payload.config.ts: -------------------------------------------------------------------------------- 1 | import { configurePayload } from './configurePayload' 2 | 3 | export default configurePayload() 4 | -------------------------------------------------------------------------------- /packages/payload/src/plugins/nestedDocsPlusPlugin/index.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | CollectionBeforeChangeHook, 3 | CollectionSlug, 4 | Config, 5 | Plugin, 6 | } from 'payload' 7 | import { nestedDocsPlugin } from '@payloadcms/plugin-nested-docs' 8 | 9 | import { isObject } from '@local/utils/isObject' 10 | 11 | const setPathnameHook: CollectionBeforeChangeHook = ({ data }) => { 12 | if ( 13 | isObject(data) && 14 | 'breadcrumbs' in data && 15 | Array.isArray(data.breadcrumbs) 16 | ) { 17 | const lastBreadcrumb: unknown = data.breadcrumbs.at(-1) 18 | 19 | if ( 20 | isObject(lastBreadcrumb) && 21 | 'url' in lastBreadcrumb && 22 | typeof lastBreadcrumb.url === 'string' 23 | ) { 24 | data.pathname = lastBreadcrumb.url.replaceAll(/^\/*/g, '/') 25 | } 26 | } 27 | } 28 | 29 | export const nestedDocsPlusPlugin = 30 | (...params: Parameters): Plugin => 31 | async incomingConfig => { 32 | const [pluginConfig] = params 33 | 34 | const nestedDocsConfig = 35 | await nestedDocsPlugin(pluginConfig)(incomingConfig) 36 | 37 | const config: Config = { 38 | ...nestedDocsConfig, 39 | collections: (nestedDocsConfig.collections ?? []).map(collection => { 40 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 41 | const fields = [...(collection.fields ?? [])] 42 | 43 | if ( 44 | pluginConfig.collections.includes(collection.slug as CollectionSlug) 45 | ) { 46 | fields.push({ 47 | admin: { 48 | position: 'sidebar', 49 | readOnly: true, 50 | }, 51 | label: 'Route Path', 52 | name: 'pathname', 53 | type: 'text', 54 | }) 55 | } 56 | 57 | return { 58 | ...collection, 59 | fields, 60 | hooks: { 61 | ...collection.hooks, 62 | beforeChange: [ 63 | ...(collection.hooks?.beforeChange ?? []), 64 | setPathnameHook, 65 | ], 66 | }, 67 | } 68 | }), 69 | onInit: async payload => { 70 | if (incomingConfig.onInit) await incomingConfig.onInit(payload) 71 | 72 | for (const collectionName of pluginConfig.collections) { 73 | const docsToUpdate = await payload.find({ 74 | collection: collectionName, 75 | depth: 1, 76 | limit: 0, 77 | where: { 78 | pathname: { 79 | exists: false, 80 | }, 81 | }, 82 | }) 83 | 84 | for (const doc of docsToUpdate.docs) { 85 | await payload.update({ 86 | collection: collectionName, 87 | data: { 88 | ...doc, 89 | }, 90 | id: doc.id, 91 | }) 92 | } 93 | } 94 | }, 95 | } 96 | 97 | return config 98 | } 99 | -------------------------------------------------------------------------------- /packages/payload/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "ES2022"], 6 | "paths": { 7 | "@local/payload/*": ["./src/*"], 8 | "@local/payload/access/*": ["./src/access/*"], 9 | "@local/payload/blocks/*": ["./src/blocks/*"], 10 | "@local/payload/collections/*": ["./src/collections/*"], 11 | "@local/payload/components/*": ["./src/components/*"], 12 | "@local/payload/fields/*": ["./src/fields/*"], 13 | "@local/payload/globals/*": ["./src/globals/*"], 14 | "@local/payload/helpers/*": ["./src/helpers/*"], 15 | "@local/payload/hooks/*": ["./src/hooks/*"], 16 | "@local/payload/payload-config": ["./src/payload.config"], 17 | "@local/payload/payload-types": ["./src/payload.types"], 18 | "@local/payload/plugins/*": ["./src/plugins/*"] 19 | }, 20 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 21 | }, 22 | "exclude": ["node_modules", "src/payload.types.ts", ".next"], 23 | "extends": "@local/tsconfig/base.json" 24 | } 25 | -------------------------------------------------------------------------------- /packages/svg/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/svg/README.md: -------------------------------------------------------------------------------- 1 | # Creating SVGs 2 | 3 | Best practices for creating SVGs. 4 | 5 | Go to [SVGR](https://react-svgr.com/playground/?exportType=named&jsxRuntime=automatic&memo=true&namedExport=ComponentName&ref=true&svgoConfig=%7B%0A%20%20%22plugins%22%3A%20%5B%0A%20%20%20%20%7B%0A%20%20%20%20%20%20%22name%22%3A%20%22preset-default%22%2C%0A%20%20%20%20%20%20%22params%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%22overrides%22%3A%20%7B%0A%20%20%20%20%20%20%20%20%20%20%22removeTitle%22%3A%20false%2C%0A%20%20%20%20%20%20%20%20%20%20%22removeViewBox%22%3A%20false%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%5D%0A%7D&typescript=true). On that page, in the sidebar change the component name from ComponentName to the name of the component that you want exported. Paste the contents of the SVG in the left pane. The resultant React component will be in the right pane. Copy the contnents and create a corresponding component in this repo and paste the contents. Then export the newly created component. 6 | -------------------------------------------------------------------------------- /packages/svg/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/react' 2 | -------------------------------------------------------------------------------- /packages/svg/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/svg", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | "./*": "./src/*/index.tsx" 9 | }, 10 | "scripts": { 11 | "clean": "rm -rf .turbo node_modules", 12 | "format": "prettier --check . --ignore-path ../../.gitignore", 13 | "lint": "eslint .", 14 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 15 | }, 16 | "prettier": "@local/prettier-config", 17 | "devDependencies": { 18 | "@local/eslint-config": "workspace:*", 19 | "@local/prettier-config": "workspace:*", 20 | "@local/tsconfig": "workspace:*", 21 | "@types/node": "^20.17.23", 22 | "@types/react": "catalog:next15", 23 | "eslint": "^9.21.0", 24 | "prettier": "^3.5.3", 25 | "react": "catalog:next15", 26 | "react-dom": "catalog:next15", 27 | "tailwindcss": "^3.4.17", 28 | "typescript": "^5.8.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/svg/src/Icon/index.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { Ref, SVGProps } from 'react' 4 | import { forwardRef, memo } from 'react' 5 | 6 | const SvgComponent = ( 7 | props: SVGProps, 8 | ref: Ref, 9 | ) => { 10 | const { color = '#42c3de', ...rest } = props 11 | return ( 12 | 23 | 24 | 25 | 29 | 33 | 34 | 35 | 36 | ) 37 | } 38 | const ForwardRef = forwardRef(SvgComponent) 39 | const Memo = memo(ForwardRef) 40 | export { Memo as Icon } 41 | -------------------------------------------------------------------------------- /packages/svg/src/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | 'use client' 2 | 3 | import type { Ref, SVGProps } from 'react' 4 | import { forwardRef, memo } from 'react' 5 | 6 | const SvgComponent = ( 7 | props: SVGProps, 8 | ref: Ref, 9 | ) => { 10 | const { color = '#42c3de', ...rest } = props 11 | return ( 12 | 23 | 24 | 25 | 29 | 33 | 34 | 35 | 36 | ) 37 | } 38 | const ForwardRef = forwardRef(SvgComponent) 39 | const Memo = memo(ForwardRef) 40 | export { Memo as Logo } 41 | -------------------------------------------------------------------------------- /packages/svg/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "ES2022"], 6 | "paths": {}, 7 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 8 | }, 9 | "exclude": ["node_modules"], 10 | "extends": "@local/tsconfig/base.json" 11 | } 12 | -------------------------------------------------------------------------------- /packages/types/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/types/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config' 2 | -------------------------------------------------------------------------------- /packages/types/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/types", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | "./*": "./src/*/index.ts" 9 | }, 10 | "scripts": { 11 | "clean": "rm -rf .turbo node_modules", 12 | "format": "prettier --check . --ignore-path ../../.gitignore", 13 | "lint": "eslint .", 14 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 15 | }, 16 | "prettier": "@local/prettier-config", 17 | "dependencies": { 18 | "zod": "^3.24.2" 19 | }, 20 | "devDependencies": { 21 | "@local/eslint-config": "workspace:*", 22 | "@local/prettier-config": "workspace:*", 23 | "@local/tsconfig": "workspace:*", 24 | "@types/node": "^20.17.23", 25 | "@types/react": "catalog:next15", 26 | "eslint": "^9.21.0", 27 | "prettier": "^3.5.3", 28 | "typescript": "^5.8.2" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/types/src/ComponentAsChild/index.ts: -------------------------------------------------------------------------------- 1 | export type ComponentAsChild = { asChild?: boolean } 2 | -------------------------------------------------------------------------------- /packages/types/src/ComponentAsChildGeneric/index.ts: -------------------------------------------------------------------------------- 1 | import type { ReactNode } from 'react' 2 | 3 | export type ComponentAsChildGeneric = 4 | | { asChild: true; children: NonNullable } 5 | | ({ 6 | asChild?: false 7 | } & TDefault) 8 | -------------------------------------------------------------------------------- /packages/types/src/ExcludeNull/index.ts: -------------------------------------------------------------------------------- 1 | export type ExcludeNull = { [P in keyof T]: Exclude } 2 | -------------------------------------------------------------------------------- /packages/types/src/Prettify/index.ts: -------------------------------------------------------------------------------- 1 | export type Prettify = { 2 | [K in keyof T]: T[K] extends object ? Prettify : T[K] 3 | // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents 4 | } & unknown 5 | -------------------------------------------------------------------------------- /packages/types/src/Redirect/index.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod' 2 | 3 | export const RedirectSchema = z.object({ 4 | destinationUrl: z.string(), 5 | includeSearchParams: z.boolean().default(true), 6 | sourceUrl: z.string(), 7 | statusCode: z 8 | .union([z.literal(301), z.literal(302), z.literal(307), z.literal(308)]) 9 | .default(302), 10 | }) 11 | 12 | export type Redirect = z.infer 13 | -------------------------------------------------------------------------------- /packages/types/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": ["dom", "dom.iterable", "ES2022"], 5 | "paths": { 6 | "@local/types/*": ["src/*/index.ts"] 7 | }, 8 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 9 | }, 10 | "exclude": ["node_modules"], 11 | "extends": "@local/tsconfig/internal-package.json" 12 | } 13 | -------------------------------------------------------------------------------- /packages/ui/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/ui/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config/react' 2 | -------------------------------------------------------------------------------- /packages/ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | "./*": "./src/components/*/index.ts", 9 | "./hooks/*": "./src/hooks/*/index.ts", 10 | "./types/*": "./src/types/*/index.ts", 11 | "./utils/*": "./src/utils/*/index.ts", 12 | "./styles.css": "./src/styles/index.css" 13 | }, 14 | "scripts": { 15 | "add": "pnpm dlx shadcn-ui add", 16 | "clean": "rm -rf .turbo node_modules", 17 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 18 | "lint": "eslint .", 19 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 20 | }, 21 | "prettier": "@local/prettier-config", 22 | "dependencies": { 23 | "@hookform/resolvers": "^3.10.0", 24 | "@local/icons": "workspace:*", 25 | "@local/svg": "workspace:*", 26 | "@local/types": "workspace:*", 27 | "@radix-ui/react-checkbox": "^1.1.4", 28 | "@radix-ui/react-dialog": "^1.1.6", 29 | "@radix-ui/react-radio-group": "^1.2.3", 30 | "@radix-ui/react-select": "^2.1.6", 31 | "@radix-ui/react-slot": "^1.1.2", 32 | "@radix-ui/react-switch": "^1.1.3", 33 | "@radix-ui/react-toggle": "^1.1.2", 34 | "class-variance-authority": "^0.7.1", 35 | "clsx": "^2.1.1", 36 | "date-fns": "^3.6.0", 37 | "embla-carousel": "^8.5.2", 38 | "embla-carousel-auto-scroll": "^8.5.2", 39 | "embla-carousel-react": "^8.5.2", 40 | "html-react-parser": "^5.2.2", 41 | "lucide-react": "^0.454.0", 42 | "next": "catalog:next15", 43 | "next-recaptcha-v3": "^1.5.2", 44 | "react": "catalog:next15", 45 | "react-device-detect": "^2.2.3", 46 | "react-dom": "catalog:next15", 47 | "react-hook-form": "^7.54.2", 48 | "react-player": "^2.16.0", 49 | "react-responsive": "^10.0.1", 50 | "tailwind-merge": "^2.6.0", 51 | "tailwindcss-animate": "^1.0.7", 52 | "ts-pattern": "^5.6.2" 53 | }, 54 | "devDependencies": { 55 | "@axa-ch/react-polymorphic-types": "^1.3.0", 56 | "@local/eslint-config": "workspace:*", 57 | "@local/prettier-config": "workspace:*", 58 | "@local/tailwind-config": "workspace:*", 59 | "@local/tsconfig": "workspace:*", 60 | "@storybook/addon-actions": "^8.6.4", 61 | "@storybook/react": "^8.6.4", 62 | "@types/node": "^20.17.23", 63 | "@types/prop-types": "^15.7.14", 64 | "@types/react": "catalog:next15", 65 | "eslint": "^9.21.0", 66 | "prettier": "^3.5.3", 67 | "tailwindcss": "^3.4.17", 68 | "typescript": "^5.8.2", 69 | "zod": "^3.24.2" 70 | }, 71 | "peerDependencies": { 72 | "@local/icons": "workspace:*", 73 | "@local/svg": "workspace:*" 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | autoprefixer: {}, 4 | tailwindcss: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /packages/ui/src/components/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/packages/ui/src/components/.gitkeep -------------------------------------------------------------------------------- /packages/ui/src/hooks/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/packages/ui/src/hooks/.gitkeep -------------------------------------------------------------------------------- /packages/ui/src/styles/index.css: -------------------------------------------------------------------------------- 1 | @import './tailwind.css'; 2 | -------------------------------------------------------------------------------- /packages/ui/src/styles/tailwind.css: -------------------------------------------------------------------------------- 1 | @tailwind utilities; 2 | 3 | @layer utilities { 4 | input[type='number'].arrow-hide::-webkit-outer-spin-button, 5 | input[type='number'].arrow-hide::-webkit-inner-spin-button, 6 | input[type='number'].arrow-hide { 7 | -webkit-appearance: none; 8 | margin: 0; 9 | -moz-appearance: textfield !important; 10 | } 11 | } 12 | 13 | html { 14 | font-size: 18px; 15 | } 16 | 17 | @media (min-width: 640px) { 18 | html { 19 | font-size: 16px; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/ui/src/types/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fusionary/turbo-payload/6d9a9368a4ebaa77713fef7b878aa19247e138ae/packages/ui/src/types/.gitkeep -------------------------------------------------------------------------------- /packages/ui/src/utils/cn/index.ts: -------------------------------------------------------------------------------- 1 | import type { ClassValue } from 'clsx' 2 | import { clsx } from 'clsx' 3 | import { extendTailwindMerge } from 'tailwind-merge' 4 | 5 | import { ExtendedConfig } from '@local/tailwind-config' 6 | 7 | const getExtendedConfigKeys = (): Record => { 8 | const mapKey = (key: string): string | undefined => { 9 | switch (key) { 10 | case 'borderRadius': 11 | case 'colors': 12 | case 'spacing': { 13 | return key 14 | } 15 | default: { 16 | return undefined 17 | } 18 | } 19 | } 20 | 21 | return Object.entries(ExtendedConfig).reduce((current, [key, value]) => { 22 | const mappedKey = mapKey(key) 23 | 24 | if (!mappedKey) { 25 | return current 26 | } 27 | 28 | return { 29 | ...current, 30 | [mappedKey]: Object.keys(value), 31 | } 32 | }, {}) 33 | } 34 | 35 | const getClassGroups = () => { 36 | const getCssProperty = (key: string): string | undefined => { 37 | switch (key) { 38 | case 'fontFamily': { 39 | return 'font-family' 40 | } 41 | case 'fontSize': { 42 | return 'font-size' 43 | } 44 | default: { 45 | return undefined 46 | } 47 | } 48 | } 49 | 50 | const mapKey = (key: string): string | undefined => { 51 | switch (key) { 52 | case 'fontFamily': { 53 | return 'font' 54 | } 55 | case 'fontSize': { 56 | return 'text' 57 | } 58 | default: { 59 | return undefined 60 | } 61 | } 62 | } 63 | 64 | return Object.entries(ExtendedConfig).reduce((current, [key, value]) => { 65 | const prop = getCssProperty(key) 66 | const mappedKey = mapKey(key) 67 | 68 | if (!prop || !mappedKey) { 69 | return current 70 | } 71 | 72 | return { 73 | ...current, 74 | [prop]: [{ [mappedKey]: Object.keys(value as Record) }], 75 | } 76 | }, {}) 77 | } 78 | 79 | const twMerge = extendTailwindMerge({ 80 | extend: { 81 | classGroups: { 82 | ...getClassGroups(), 83 | }, 84 | theme: { 85 | ...getExtendedConfigKeys(), 86 | }, 87 | }, 88 | prefix: undefined, 89 | }) 90 | 91 | export const cn = (...inputs: ClassValue[]): string => twMerge(clsx(inputs)) 92 | -------------------------------------------------------------------------------- /packages/ui/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is not used for any compilation purpose, it is only used 3 | * for Tailwind Intellisense & Autocompletion in the source files 4 | */ 5 | import type { Config } from 'tailwindcss' 6 | 7 | import baseConfig from '@local/tailwind-config' 8 | 9 | export default { 10 | content: baseConfig.content, 11 | presets: [baseConfig], 12 | } satisfies Config 13 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "jsx": "preserve", 5 | "lib": ["dom", "dom.iterable", "ES2022"], 6 | "paths": { 7 | "@local/ui/*": ["src/components/*/index.ts"], 8 | "@local/ui/cn": ["src/utils/cn/index.ts"], 9 | "@local/ui/hooks/*": ["src/hooks/*/index.ts"], 10 | "@local/ui/types/*": ["src/types/*/index.ts"], 11 | "@local/ui/utils/*": ["src/utils/*/index.ts"] 12 | }, 13 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 14 | }, 15 | "exclude": ["node_modules"], 16 | "extends": "@local/tsconfig/internal-package.json" 17 | } 18 | -------------------------------------------------------------------------------- /packages/utils/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /packages/utils/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config' 2 | -------------------------------------------------------------------------------- /packages/utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/utils", 3 | "version": "0.0.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | "./*": "./src/*/index.ts" 9 | }, 10 | "scripts": { 11 | "clean": "rm -rf .turbo node_modules", 12 | "format": "prettier --check . --ignore-path ../../.gitignore", 13 | "lint": "eslint .", 14 | "typecheck": "tsc --noEmit --emitDeclarationOnly false" 15 | }, 16 | "prettier": "@local/prettier-config", 17 | "dependencies": { 18 | "@local/types": "workspace:*", 19 | "slugify": "^1.6.6", 20 | "zod": "^3.24.2" 21 | }, 22 | "devDependencies": { 23 | "@local/eslint-config": "workspace:*", 24 | "@local/prettier-config": "workspace:*", 25 | "@local/tsconfig": "workspace:*", 26 | "@types/node": "^20.17.23", 27 | "eslint": "^9.21.0", 28 | "prettier": "^3.5.3", 29 | "typescript": "^5.8.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/utils/src/chunkArray/index.ts: -------------------------------------------------------------------------------- 1 | export function* chunkArray(arr: T[], size: number): Generator { 2 | for (let i = 0; i < arr.length; i += size) { 3 | yield arr.slice(i, i + size) 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/utils/src/createSlug/index.ts: -------------------------------------------------------------------------------- 1 | import slugify from 'slugify' 2 | 3 | export function createSlug(name: string): string { 4 | return slugify(name, { lower: true }) 5 | } 6 | -------------------------------------------------------------------------------- /packages/utils/src/fetchWithZod/index.ts: -------------------------------------------------------------------------------- 1 | import type { z } from 'zod' 2 | 3 | export type FetchWithZodResult = Awaited< 4 | ReturnType 5 | > 6 | 7 | type FetchParams = Parameters 8 | 9 | export const fetchWithZod = async ( 10 | successSchema: TSuccess, 11 | ...params: FetchParams 12 | ): Promise> => { 13 | const response = await fetch(...params) 14 | 15 | if (!response.ok) { 16 | throw new Error(response.statusText) 17 | } 18 | 19 | const data: unknown = await response.json() 20 | 21 | return (await successSchema.safeParseAsync( 22 | data, 23 | )) as FetchWithZodResult 24 | } 25 | -------------------------------------------------------------------------------- /packages/utils/src/isObject/index.ts: -------------------------------------------------------------------------------- 1 | export const isObject = (val: unknown): val is Record => 2 | typeof val === 'object' && val !== null 3 | -------------------------------------------------------------------------------- /packages/utils/src/sleep/index.ts: -------------------------------------------------------------------------------- 1 | export const sleep = (ms: number) => 2 | new Promise(resolve => setTimeout(resolve, ms)) 3 | -------------------------------------------------------------------------------- /packages/utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "lib": ["dom", "dom.iterable", "ES2022"], 5 | "paths": { 6 | "@local/utils/*": ["src/*/index.ts"] 7 | }, 8 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 9 | }, 10 | "exclude": ["node_modules"], 11 | "extends": "@local/tsconfig/base.json" 12 | } 13 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - apps/* 3 | - packages/* 4 | - tooling/* 5 | 6 | catalogs: 7 | next15: 8 | next: ^15.2.0 9 | react: ^19.0.0 10 | react-dom: ^19.0.0 11 | '@types/react': ^19.0.1 12 | '@types/react-dom': ^19.0.1 13 | -------------------------------------------------------------------------------- /tooling/eslint/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tooling/eslint/astro.mjs: -------------------------------------------------------------------------------- 1 | import base from '@local/eslint-config' 2 | 3 | /** @typedef {import('eslint').Linter.Config} Config */ 4 | 5 | /** @type {Config[]} */ 6 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 7 | const baseConfig = base 8 | 9 | /** @type {Config[]} */ 10 | const _default = [ 11 | { 12 | ignores: ['.astro/**/*', 'src/env.d.ts'], 13 | }, 14 | ...baseConfig, 15 | ] 16 | 17 | export default _default 18 | -------------------------------------------------------------------------------- /tooling/eslint/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | // @ts-expect-error - no types 2 | export { default } from '@fusionary/eslint-config' 3 | -------------------------------------------------------------------------------- /tooling/eslint/next.mjs: -------------------------------------------------------------------------------- 1 | // @ts-expect-error - no types 2 | export { default } from '@fusionary/eslint-config/next' 3 | -------------------------------------------------------------------------------- /tooling/eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/eslint-config", 3 | "version": "0.2.0", 4 | "private": true, 5 | "license": "MIT", 6 | "exports": { 7 | ".": "./eslint.config.mjs", 8 | "./astro": "./astro.mjs", 9 | "./next": "./next.mjs", 10 | "./react": "./react.mjs", 11 | "./svelte": "./svelte.mjs" 12 | }, 13 | "scripts": { 14 | "clean": "rm -rf .turbo node_modules", 15 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 16 | "lint": "eslint .", 17 | "typecheck": "tsc --noEmit" 18 | }, 19 | "prettier": "@local/prettier-config", 20 | "dependencies": { 21 | "@fusionary/eslint-config": "^5.1.6", 22 | "astro-eslint-parser": "^1.2.1", 23 | "eslint-config-prettier": "^9.1.0", 24 | "eslint-plugin-astro": "^1.3.1", 25 | "eslint-plugin-svelte": "^2.46.1", 26 | "typescript-eslint": "^8.26.0" 27 | }, 28 | "devDependencies": { 29 | "@local/prettier-config": "workspace:*", 30 | "@local/tsconfig": "workspace:*", 31 | "@types/eslint": "^9.6.1", 32 | "@types/eslint__js": "^8.42.3", 33 | "@types/node": "^20.17.23", 34 | "eslint": "^9.21.0", 35 | "globals": "^15.15.0", 36 | "prettier": "^3.5.3", 37 | "tailwindcss": "^3.4.17" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /tooling/eslint/react.mjs: -------------------------------------------------------------------------------- 1 | // @ts-expect-error - no types 2 | export { default } from '@fusionary/eslint-config/react' 3 | -------------------------------------------------------------------------------- /tooling/eslint/svelte.mjs: -------------------------------------------------------------------------------- 1 | // @ts-expect-error - no types 2 | import eslintConfigPrettier from 'eslint-config-prettier' 3 | import svelte from 'eslint-plugin-svelte' 4 | import globals from 'globals' 5 | import { parser } from 'typescript-eslint' 6 | 7 | // eslint-disable-next-line @typescript-eslint/ban-ts-comment 8 | // @ts-ignore 9 | import base from '@local/eslint-config' 10 | 11 | /** @typedef {import('eslint').Linter.Config} Config */ 12 | 13 | /** @type {Config[]} */ 14 | // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment 15 | const baseConfig = base 16 | 17 | /** @type {Config[]} */ 18 | const _default = [ 19 | { 20 | ignores: ['build/', '.svelte-kit/', 'dist/'], 21 | }, 22 | ...baseConfig, 23 | ...svelte.configs['flat/recommended'], 24 | eslintConfigPrettier, 25 | ...svelte.configs['flat/prettier'], 26 | { 27 | languageOptions: { 28 | globals: { 29 | ...globals.browser, 30 | ...globals.node, 31 | }, 32 | }, 33 | }, 34 | { 35 | files: ['**/*.svelte'], 36 | languageOptions: { 37 | parserOptions: { 38 | extraFileExtensions: ['.svelte'], 39 | parser, 40 | }, 41 | }, 42 | }, 43 | ] 44 | 45 | export default _default 46 | -------------------------------------------------------------------------------- /tooling/eslint/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 4 | }, 5 | "exclude": ["node_modules"], 6 | "extends": "@local/tsconfig/base.json", 7 | "include": ["."] 8 | } 9 | -------------------------------------------------------------------------------- /tooling/github/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tooling/github/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/github" 3 | } 4 | -------------------------------------------------------------------------------- /tooling/github/setup/action.yml: -------------------------------------------------------------------------------- 1 | name: 'Setup and install' 2 | description: 'Common setup steps for Actions' 3 | 4 | runs: 5 | using: composite 6 | steps: 7 | - uses: pnpm/action-setup@v4 8 | with: 9 | run_install: false 10 | 11 | - uses: actions/setup-node@v4 12 | with: 13 | node-version: 20 14 | cache: 'pnpm' 15 | 16 | - name: Install Turbo 17 | shell: bash 18 | run: pnpm add -g turbo 19 | 20 | - name: pnpm install 21 | shell: bash 22 | run: pnpm install 23 | -------------------------------------------------------------------------------- /tooling/prettier/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tooling/prettier/index.js: -------------------------------------------------------------------------------- 1 | const path = require('node:path') 2 | // @ts-expect-error - no types 3 | const base = require('@fusionary/prettier-config') 4 | 5 | const { join } = path 6 | 7 | /** @typedef {import("prettier").Config} PrettierConfig */ 8 | /** @typedef {import("prettier-plugin-tailwindcss").PluginOptions} TailwindConfig */ 9 | 10 | /** @type { PrettierConfig | TailwindConfig } */ 11 | module.exports = { 12 | ...base, 13 | plugins: [...base.plugins, require.resolve('prettier-plugin-tailwindcss')], 14 | tailwindConfig: join(__dirname, '../../tooling/tailwind/src/tailwind.ts'), 15 | tailwindFunctions: ['classNames', 'clsx', 'cn', 'cva', 'tv', 'tw', 'twMerge'], 16 | } 17 | -------------------------------------------------------------------------------- /tooling/prettier/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/prettier-config", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "commonjs", 6 | "exports": { 7 | ".": "./index.js" 8 | }, 9 | "scripts": { 10 | "clean": "rm -rf .turbo node_modules", 11 | "format": "prettier --check . --ignore-path ../../.gitignore --ignore-path ./.prettierignore", 12 | "typecheck": "tsc --noEmit" 13 | }, 14 | "prettier": "@local/prettier-config", 15 | "dependencies": { 16 | "@fusionary/prettier-config": "^5.1.6", 17 | "prettier-plugin-tailwindcss": "^0.6.11" 18 | }, 19 | "devDependencies": { 20 | "@local/tsconfig": "workspace:*", 21 | "@types/node": "^20.17.23", 22 | "typescript": "^5.8.2" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tooling/prettier/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 4 | }, 5 | "exclude": ["node_modules"], 6 | "extends": "@local/tsconfig/base.json" 7 | } 8 | -------------------------------------------------------------------------------- /tooling/tailwind/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | export { default } from '@local/eslint-config' 2 | -------------------------------------------------------------------------------- /tooling/tailwind/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/tailwind-config", 3 | "version": "0.1.0", 4 | "private": true, 5 | "license": "MIT", 6 | "type": "module", 7 | "exports": { 8 | ".": "./src/tailwind.ts" 9 | }, 10 | "scripts": { 11 | "clean": "rm -rf .turbo node_modules", 12 | "format": "prettier --check . --ignore-path ../../.gitignore", 13 | "lint": "eslint .", 14 | "typecheck": "tsc --noEmit" 15 | }, 16 | "prettier": "@local/prettier-config", 17 | "dependencies": { 18 | "autoprefixer": "^10.4.20", 19 | "postcss": "^8.5.3", 20 | "tailwindcss": "^3.4.17", 21 | "tailwindcss-animate": "^1.0.7" 22 | }, 23 | "devDependencies": { 24 | "@local/eslint-config": "workspace:*", 25 | "@local/prettier-config": "workspace:*", 26 | "@local/tsconfig": "workspace:*", 27 | "eslint": "^9.21.0", 28 | "prettier": "^3.5.3", 29 | "typescript": "^5.8.2" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tooling/tailwind/src/colors.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeConfig } from './types' 2 | 3 | export const colors: ThemeConfig['colors'] = {} 4 | -------------------------------------------------------------------------------- /tooling/tailwind/src/tailwind.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'tailwindcss' 2 | 3 | import { colors } from './colors' 4 | import { fontFamily, fontSize } from './typography' 5 | import { borderRadius, spacing } from './ui' 6 | 7 | export const ExtendedConfig = { 8 | borderRadius, 9 | colors, 10 | fontFamily, 11 | fontSize, 12 | spacing, 13 | } 14 | 15 | export default { 16 | content: ['./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}'], 17 | darkMode: ['class', '[data-mode="dark"]'], 18 | theme: { 19 | extend: { 20 | ...ExtendedConfig, 21 | }, 22 | }, 23 | } as const satisfies Config 24 | -------------------------------------------------------------------------------- /tooling/tailwind/src/types.ts: -------------------------------------------------------------------------------- 1 | export { type ThemeConfig } from 'tailwindcss/types/config' 2 | -------------------------------------------------------------------------------- /tooling/tailwind/src/typography.ts: -------------------------------------------------------------------------------- 1 | import defaultTheme from 'tailwindcss/defaultTheme' 2 | 3 | import type { ThemeConfig } from './types' 4 | 5 | export const fontFamily: ThemeConfig['fontFamily'] = { 6 | sans: [...defaultTheme.fontFamily.sans], 7 | serif: [...defaultTheme.fontFamily.serif], 8 | } 9 | 10 | export const fontSize: ThemeConfig['fontSize'] = {} 11 | -------------------------------------------------------------------------------- /tooling/tailwind/src/ui.ts: -------------------------------------------------------------------------------- 1 | import type { ThemeConfig } from './types' 2 | 3 | export const spacing: ThemeConfig['spacing'] = {} 4 | 5 | export const borderRadius: ThemeConfig['borderRadius'] = {} 6 | -------------------------------------------------------------------------------- /tooling/tailwind/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "tsBuildInfoFile": "node_modules/.cache/tsbuildinfo.json" 4 | }, 5 | "exclude": ["node_modules"], 6 | "extends": "@local/tsconfig/base.json" 7 | } 8 | -------------------------------------------------------------------------------- /tooling/typescript/.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.json": [ 3 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 4 | ], 5 | "*.{js,ts,tsx,cjs,mjs}": [ 6 | "eslint --fix", 7 | "prettier --write --check --ignore-path ../../.gitignore --ignore-path ./.prettierignore" 8 | ] 9 | } 10 | -------------------------------------------------------------------------------- /tooling/typescript/base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@fusionary/tsconfig/base.json" 4 | } 5 | -------------------------------------------------------------------------------- /tooling/typescript/internal-package.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "extends": "@fusionary/tsconfig/internal-package.json" 4 | } 5 | -------------------------------------------------------------------------------- /tooling/typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@local/tsconfig", 3 | "version": "0.1.0", 4 | "private": true, 5 | "files": [ 6 | "*.json" 7 | ], 8 | "dependencies": { 9 | "@fusionary/tsconfig": "^5.1.6" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turborepo.org/schema.json", 3 | "globalDependencies": ["**/.env", "**/.env.development", "**/.env.prod"], 4 | "globalEnv": [ 5 | "CI", 6 | "NODE_ENV", 7 | "NEXT_PUBLIC_APP_ENV", 8 | "NEXT_PUBLIC_PAYLOAD_URL", 9 | "NEXT_PUBLIC_SITE_URL", 10 | "PAYLOAD_PRIVATE_DATABASE_URI", 11 | "PAYLOAD_PRIVATE_SECRET", 12 | "PAYLOAD_PRIVATE_REVALIDATION_KEY", 13 | "SKIP_ENV_VALIDATION", 14 | "npm_lifecycle_event" 15 | ], 16 | "remoteCache": { 17 | "enabled": true 18 | }, 19 | "tasks": { 20 | "//#clean": { 21 | "cache": false 22 | }, 23 | "build": { 24 | "dependsOn": ["^build"], 25 | "outputs": [ 26 | ".next/**", 27 | "!.next/cache/**", 28 | "next-env.d.ts", 29 | ".expo/**", 30 | ".output/**", 31 | ".vercel/output/**", 32 | "storybook-static/**", 33 | "dist/**" 34 | ] 35 | }, 36 | "clean": { 37 | "cache": false 38 | }, 39 | "dev": { 40 | "cache": false, 41 | "persistent": true 42 | }, 43 | "format": { 44 | "outputLogs": "new-only", 45 | "outputs": ["node_modules/.cache/.prettiercache"] 46 | }, 47 | "lint": { 48 | "dependsOn": ["^topo"], 49 | "outputs": ["node_modules/.cache/.eslintcache"] 50 | }, 51 | "storybook": { 52 | "cache": true, 53 | "persistent": true 54 | }, 55 | "topo": { 56 | "dependsOn": ["^topo"] 57 | }, 58 | "typecheck": { 59 | "dependsOn": ["^topo"], 60 | "outputs": ["node_modules/.cache/tsbuildinfo.json"] 61 | } 62 | }, 63 | "ui": "tui" 64 | } 65 | --------------------------------------------------------------------------------