@next-friday/create-app 2.0.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/README.md +16 -15
  2. package/dist/main.mjs +18 -7
  3. package/package.json +1 -1
  4. package/templates/base/pnpm-workspace.yaml +24 -2
  5. package/templates/payload/README.md +1 -1
  6. package/templates/payload/package.json +0 -1
  7. package/templates/payload/src/payload.config.test.ts +31 -0
  8. package/templates/turborepo/.agents/rules/typescript-first.md +7 -0
  9. package/templates/turborepo/.agents/rules.json +41 -0
  10. package/templates/turborepo/.claude/rules/deterministic-testing.md +21 -0
  11. package/templates/turborepo/.claude/rules/typescript-first.md +15 -0
  12. package/templates/turborepo/.cursor/rules/deterministic-testing.mdc +21 -0
  13. package/templates/turborepo/.cursor/rules/typescript-first.mdc +13 -0
  14. package/templates/turborepo/.dockerignore +12 -0
  15. package/templates/turborepo/.editorconfig-checker.json +3 -0
  16. package/templates/turborepo/.env.example +4 -0
  17. package/templates/turborepo/.github/ISSUE_TEMPLATE/bug.yml +66 -0
  18. package/templates/turborepo/.github/ISSUE_TEMPLATE/feature.yml +51 -0
  19. package/templates/turborepo/.github/dependabot.yml +79 -0
  20. package/templates/turborepo/.github/labeler.yml +25 -0
  21. package/templates/turborepo/.github/pull_request_template.md +25 -0
  22. package/templates/turborepo/.github/workflows/ci.yml +212 -0
  23. package/templates/turborepo/.github/workflows/deploy.yml +135 -0
  24. package/templates/turborepo/.github/workflows/sast.yml +46 -0
  25. package/templates/turborepo/.lintstagedrc.json +10 -0
  26. package/templates/turborepo/.prettierignore +5 -0
  27. package/templates/turborepo/AGENTS.md +20 -0
  28. package/templates/turborepo/CONTRIBUTING.md +58 -0
  29. package/templates/turborepo/README.md +102 -0
  30. package/templates/turborepo/_gitignore +53 -0
  31. package/templates/turborepo/apps/app/.dependency-cruiser.json +34 -0
  32. package/templates/turborepo/apps/app/Dockerfile +25 -0
  33. package/templates/turborepo/apps/app/app/favicon.ico +0 -0
  34. package/templates/turborepo/apps/app/app/globals.css +26 -0
  35. package/templates/turborepo/apps/app/app/health/route.test.ts +20 -0
  36. package/templates/turborepo/apps/app/app/health/route.ts +23 -0
  37. package/templates/turborepo/apps/app/app/layout.tsx +29 -0
  38. package/templates/turborepo/apps/app/app/page.test.ts +11 -0
  39. package/templates/turborepo/apps/app/app/page.tsx +8 -0
  40. package/templates/turborepo/apps/app/next.config.ts +40 -0
  41. package/templates/turborepo/apps/app/package.json +41 -0
  42. package/templates/turborepo/apps/app/playwright.config.ts +27 -0
  43. package/templates/turborepo/apps/app/postcss.config.mjs +7 -0
  44. package/templates/turborepo/apps/app/test/home.e2e.test.ts +11 -0
  45. package/templates/turborepo/apps/app/tsconfig.json +33 -0
  46. package/templates/turborepo/apps/app/vitest.config.ts +12 -0
  47. package/templates/turborepo/apps/backoffice/.dependency-cruiser.json +34 -0
  48. package/templates/turborepo/apps/backoffice/Dockerfile +28 -0
  49. package/templates/turborepo/apps/backoffice/next.config.ts +48 -0
  50. package/templates/turborepo/apps/backoffice/package.json +49 -0
  51. package/templates/turborepo/apps/backoffice/playwright.config.ts +27 -0
  52. package/templates/turborepo/apps/backoffice/src/app/(frontend)/layout.tsx +18 -0
  53. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.test.ts +14 -0
  54. package/templates/turborepo/apps/backoffice/src/app/(frontend)/page.tsx +14 -0
  55. package/templates/turborepo/apps/backoffice/src/app/(frontend)/styles.css +15 -0
  56. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/[[...segments]]/not-found.tsx +24 -0
  57. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/[[...segments]]/page.tsx +24 -0
  58. package/templates/turborepo/apps/backoffice/src/app/(payload)/admin/importMap.ts +6 -0
  59. package/templates/turborepo/apps/backoffice/src/app/(payload)/api/[...slug]/route.ts +19 -0
  60. package/templates/turborepo/apps/backoffice/src/app/(payload)/custom.scss +0 -0
  61. package/templates/turborepo/apps/backoffice/src/app/(payload)/layout.tsx +31 -0
  62. package/templates/turborepo/apps/backoffice/src/app/health/route.test.ts +20 -0
  63. package/templates/turborepo/apps/backoffice/src/app/health/route.ts +23 -0
  64. package/templates/turborepo/apps/backoffice/src/collections/media.test.ts +27 -0
  65. package/templates/turborepo/apps/backoffice/src/collections/media.ts +18 -0
  66. package/templates/turborepo/apps/backoffice/src/collections/users.test.ts +10 -0
  67. package/templates/turborepo/apps/backoffice/src/collections/users.ts +10 -0
  68. package/templates/turborepo/apps/backoffice/src/environment.test.ts +32 -0
  69. package/templates/turborepo/apps/backoffice/src/environment.ts +27 -0
  70. package/templates/turborepo/apps/backoffice/src/migrations/20261030_005055_initial.json +969 -0
  71. package/templates/turborepo/apps/backoffice/src/migrations/20261030_005055_initial.ts +134 -0
  72. package/templates/turborepo/apps/backoffice/src/migrations/index.ts +9 -0
  73. package/templates/turborepo/apps/backoffice/src/payload-types.ts +339 -0
  74. package/templates/turborepo/apps/backoffice/src/payload.config.test.ts +31 -0
  75. package/templates/turborepo/apps/backoffice/src/payload.config.ts +50 -0
  76. package/templates/turborepo/apps/backoffice/test/home.e2e.test.ts +16 -0
  77. package/templates/turborepo/apps/backoffice/tsconfig.json +34 -0
  78. package/templates/turborepo/apps/backoffice/vitest.config.ts +19 -0
  79. package/templates/turborepo/eslint.config.ts +40 -0
  80. package/templates/turborepo/knip.json +9 -0
  81. package/templates/turborepo/package.json +70 -0
  82. package/templates/turborepo/pnpm-workspace.yaml +45 -0
  83. package/templates/turborepo/prettier.config.ts +11 -0
  84. package/templates/turborepo/tsconfig.json +16 -0
  85. package/templates/turborepo/turbo.json +50 -0
@@ -0,0 +1,24 @@
1
+ /* THIS FILE WAS GENERATED AUTOMATICALLY BY PAYLOAD. */
2
+ /* DO NOT MODIFY IT BECAUSE IT COULD BE REWRITTEN AT ANY TIME. */
3
+ import type { Metadata } from 'next'
4
+
5
+ import config from '@payload-config'
6
+ import { RootPage, generatePageMetadata } from '@payloadcms/next/views'
7
+ import { importMap } from '../importMap'
8
+
9
+ type Args = {
10
+ params: Promise<{
11
+ segments: string[]
12
+ }>
13
+ searchParams: Promise<{
14
+ [key: string]: string | string[]
15
+ }>
16
+ }
17
+
18
+ export const generateMetadata = ({ params, searchParams }: Args): Promise<Metadata> =>
19
+ generatePageMetadata({ config, params, searchParams })
20
+
21
+ const Page = ({ params, searchParams }: Args) =>
22
+ RootPage({ config, params, searchParams, importMap })
23
+
24
+ export default Page
@@ -0,0 +1,6 @@
1
+ import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
2
+
3
+ /** @type import('payload').ImportMap */
4
+ export const importMap = {
5
+ "@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
6
+ }
@@ -0,0 +1,19 @@
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
+ import '@payloadcms/next/css'
5
+ import {
6
+ REST_DELETE,
7
+ REST_GET,
8
+ REST_OPTIONS,
9
+ REST_PATCH,
10
+ REST_POST,
11
+ REST_PUT,
12
+ } from '@payloadcms/next/routes'
13
+
14
+ export const GET = REST_GET(config)
15
+ export const POST = REST_POST(config)
16
+ export const DELETE = REST_DELETE(config)
17
+ export const PATCH = REST_PATCH(config)
18
+ export const PUT = REST_PUT(config)
19
+ export const OPTIONS = REST_OPTIONS(config)
@@ -0,0 +1,31 @@
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
+ import '@payloadcms/next/css'
5
+ import type { ServerFunctionClient } from 'payload'
6
+ import { handleServerFunctions, RootLayout } from '@payloadcms/next/layouts'
7
+ import React from 'react'
8
+
9
+ import { importMap } from './admin/importMap'
10
+ import './custom.scss'
11
+
12
+ type Args = {
13
+ children: React.ReactNode
14
+ }
15
+
16
+ const serverFunction: ServerFunctionClient = async function (args) {
17
+ 'use server'
18
+ return handleServerFunctions({
19
+ ...args,
20
+ config,
21
+ importMap,
22
+ })
23
+ }
24
+
25
+ const Layout = ({ children }: Args) => (
26
+ <RootLayout config={config} importMap={importMap} serverFunction={serverFunction}>
27
+ {children}
28
+ </RootLayout>
29
+ )
30
+
31
+ export default Layout
@@ -0,0 +1,20 @@
1
+ import {describe, expect, it, vi} from "vitest";
2
+
3
+ import {GET} from "./route";
4
+
5
+ vi.mock("next/server", () => ({
6
+ connection: vi.fn(async () => {}),
7
+ }));
8
+
9
+ describe("GET /health", () => {
10
+ it("reports ok without caching", async () => {
11
+ const response = await GET();
12
+
13
+ expect(response.status).toBe(200);
14
+ expect(response.headers.get("Cache-Control")).toBe("no-store");
15
+
16
+ await expect(response.json()).resolves.toEqual({
17
+ status: "ok",
18
+ });
19
+ });
20
+ });
@@ -0,0 +1,23 @@
1
+ import {connection} from "next/server";
2
+
3
+ /**
4
+ * Report that the server is up, for platform health checks. Rendered per request and never cached, so a
5
+ * stopped server cannot pass from a cached response.
6
+ * @returns A JSON response with `status: "ok"`.
7
+ */
8
+ async function get(): Promise<Response> {
9
+ await connection();
10
+
11
+ return Response.json(
12
+ {
13
+ status: "ok",
14
+ },
15
+ {
16
+ headers: {
17
+ "Cache-Control": "no-store",
18
+ },
19
+ },
20
+ );
21
+ }
22
+
23
+ export {get as GET};
@@ -0,0 +1,27 @@
1
+ import {describe, expect, it} from "vitest";
2
+ import type {PayloadRequest} from "payload";
3
+
4
+ import {Media} from "./media";
5
+
6
+ describe("Media", () => {
7
+ it("lets anyone read uploaded files", () => {
8
+ const read = Media.access?.read;
9
+
10
+ expect(read).toBeTypeOf("function");
11
+
12
+ expect(
13
+ read?.({
14
+ req: {} as PayloadRequest,
15
+ }),
16
+ ).toBe(true);
17
+ });
18
+
19
+ it("requires alt text", () => {
20
+ expect(Media.fields).toContainEqual(
21
+ expect.objectContaining({
22
+ name: "alt",
23
+ required: true,
24
+ }),
25
+ );
26
+ });
27
+ });
@@ -0,0 +1,18 @@
1
+ import type {CollectionConfig} from "payload";
2
+
3
+ export const Media: CollectionConfig = {
4
+ slug: "media",
5
+ fields: [
6
+ {
7
+ name: "alt",
8
+ type: "text",
9
+ required: true,
10
+ },
11
+ ],
12
+ upload: {
13
+ mimeTypes: ["image/*"],
14
+ },
15
+ access: {
16
+ read: () => true,
17
+ },
18
+ };
@@ -0,0 +1,10 @@
1
+ import {describe, expect, it} from "vitest";
2
+
3
+ import {Users} from "./users";
4
+
5
+ describe("Users", () => {
6
+ it("enables authentication and titles users by email", () => {
7
+ expect(Users.auth).toBe(true);
8
+ expect(Users.admin?.useAsTitle).toBe("email");
9
+ });
10
+ });
@@ -0,0 +1,10 @@
1
+ import type {CollectionConfig} from "payload";
2
+
3
+ export const Users: CollectionConfig = {
4
+ slug: "users",
5
+ auth: true,
6
+ fields: [],
7
+ admin: {
8
+ useAsTitle: "email",
9
+ },
10
+ };
@@ -0,0 +1,32 @@
1
+ import {afterEach, describe, expect, it, vi} from "vitest";
2
+ import {assert, property, string} from "fast-check";
3
+
4
+ import {requireEnvironment} from "./environment";
5
+
6
+ afterEach(() => {
7
+ vi.unstubAllEnvs();
8
+ });
9
+
10
+ describe("requireEnvironment", () => {
11
+ it("returns any non-empty value unchanged", () => {
12
+ assert(
13
+ property(
14
+ string({
15
+ minLength: 1,
16
+ }).filter(value => !value.includes("\0")),
17
+ value => {
18
+ vi.stubEnv("EXAMPLE_VALUE", value);
19
+ expect(requireEnvironment("EXAMPLE_VALUE")).toBe(value);
20
+ },
21
+ ),
22
+ );
23
+ });
24
+
25
+ it.each([undefined, ""])("throws when the value is %j", value => {
26
+ vi.stubEnv("EXAMPLE_VALUE", value);
27
+
28
+ expect(() => requireEnvironment("EXAMPLE_VALUE")).toThrow(
29
+ "EXAMPLE_VALUE environment variable is required",
30
+ );
31
+ });
32
+ });
@@ -0,0 +1,27 @@
1
+ import {existsSync} from "node:fs";
2
+ import {fileURLToPath} from "node:url";
3
+ import path from "node:path";
4
+
5
+ /**
6
+ * Read a required environment variable.
7
+ * @param name - Variable name.
8
+ * @returns The variable's value.
9
+ */
10
+ export function requireEnvironment(name: string): string {
11
+ const environmentFile = path.resolve(
12
+ path.dirname(fileURLToPath(import.meta.url)),
13
+ "../../../.env",
14
+ );
15
+
16
+ if (existsSync(environmentFile)) {
17
+ process.loadEnvFile(environmentFile);
18
+ }
19
+
20
+ const value = process.env[name];
21
+
22
+ if (value === undefined || value === "") {
23
+ throw new Error(`${name} environment variable is required`);
24
+ }
25
+
26
+ return value;
27
+ }