diff --git a/framework-boilerplates/redwoodjs/.env.defaults b/framework-boilerplates/redwoodjs/.env.defaults index 550024380e..fb88fb33b3 100644 --- a/framework-boilerplates/redwoodjs/.env.defaults +++ b/framework-boilerplates/redwoodjs/.env.defaults @@ -11,3 +11,9 @@ DATABASE_URL=file:./dev.db # disables Prisma CLI update notifier PRISMA_HIDE_UPDATE_MESSAGE=true + +# Option to override the current environment's default api-side log level +# See: https://redwoodjs.com/docs/logger for level options, defaults to "trace" otherwise. +# Most applications want "debug" or "info" during dev, "trace" when you have issues and "warn" in production. +# Ordered by how verbose they are: trace | debug | info | warn | error | silent +# LOG_LEVEL=debug diff --git a/framework-boilerplates/redwoodjs/.env.example b/framework-boilerplates/redwoodjs/.env.example new file mode 100644 index 0000000000..2a2de6c026 --- /dev/null +++ b/framework-boilerplates/redwoodjs/.env.example @@ -0,0 +1,4 @@ +# DATABASE_URL=file:./dev.db +# TEST_DATABASE_URL=file:./.redwood/test.db +# PRISMA_HIDE_UPDATE_MESSAGE=true +# LOG_LEVEL=trace diff --git a/framework-boilerplates/redwoodjs/.gitignore b/framework-boilerplates/redwoodjs/.gitignore index 9fde2b3d29..8b8632fd5d 100644 --- a/framework-boilerplates/redwoodjs/.gitignore +++ b/framework-boilerplates/redwoodjs/.gitignore @@ -1,13 +1,25 @@ .idea .DS_Store -.env +.env* +!.env.example +!.env.defaults .netlify -.redwood -dev.db +.redwood/* +!.redwood/README.md +dev.db* dist dist-babel node_modules yarn-error.log web/public/mockServiceWorker.js - -.vercel \ No newline at end of file +web/types/graphql.d.ts +api/types/graphql.d.ts +api/src/lib/generateGraphiQLHeader.* +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/sdks +!.yarn/versions +.vercel diff --git a/framework-boilerplates/redwoodjs/.yarnrc.yml b/framework-boilerplates/redwoodjs/.yarnrc.yml new file mode 100644 index 0000000000..e8c5d50aa7 --- /dev/null +++ b/framework-boilerplates/redwoodjs/.yarnrc.yml @@ -0,0 +1,15 @@ +# Yarn's manifest file. You can configure yarn here. +# See https://yarnpkg.com/configuration/yarnrc. + +# For `node_modules` (see `nodeLinker` below), this is almost always the preferred option. +compressionLevel: 0 + +enableGlobalCache: true + +# Lets yarn use hardlinks inside `node_modules` to dedupe packages. +# For a more pnpm-like experience, consider `hardlinks-global` where hardlinks point to a global store. +nmMode: hardlinks-local + +# How to install Node packages. +# Heads up: right now, Redwood expects this to be `node-modules`. +nodeLinker: node-modules diff --git a/framework-boilerplates/redwoodjs/README.md b/framework-boilerplates/redwoodjs/README.md index 00c65c71a6..438899ced1 100644 --- a/framework-boilerplates/redwoodjs/README.md +++ b/framework-boilerplates/redwoodjs/README.md @@ -19,3 +19,5 @@ To get started with RedwoodJS on Vercel, you can [use Yarn to initialize](https: ```shell $ yarn create redwood-app ./my-redwood-app ``` + +This repository enables the [Corepack](https://vercel.com/docs/builds/configure-a-build#corepack) using the [build.env](https://vercel.com/docs/project-configuration#build.env) property in the `vercel.json` file. However, we recommend adding `ENABLE_EXPERIMENTAL_COREPACK=1` as an Environment Variable in your project settings instead. \ No newline at end of file diff --git a/framework-boilerplates/redwoodjs/api/.babelrc.js b/framework-boilerplates/redwoodjs/api/.babelrc.js deleted file mode 100644 index 73439347d4..0000000000 --- a/framework-boilerplates/redwoodjs/api/.babelrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = { extends: '../babel.config.js' } diff --git a/framework-boilerplates/redwoodjs/api/db/schema.prisma b/framework-boilerplates/redwoodjs/api/db/schema.prisma index 291504d62e..8c8666606f 100644 --- a/framework-boilerplates/redwoodjs/api/db/schema.prisma +++ b/framework-boilerplates/redwoodjs/api/db/schema.prisma @@ -1,4 +1,10 @@ -datasource DS { +// Don't forget to tell Prisma about your edits to this file using +// `yarn rw prisma migrate dev` or `yarn rw prisma db push`. +// `migrate` is like committing while `push` is for prototyping. +// Read more about both here: +// https://www.prisma.io/docs/orm/prisma-migrate + +datasource db { provider = "sqlite" url = env("DATABASE_URL") } diff --git a/framework-boilerplates/redwoodjs/api/db/seed.js b/framework-boilerplates/redwoodjs/api/db/seed.js deleted file mode 100644 index bec5d1021b..0000000000 --- a/framework-boilerplates/redwoodjs/api/db/seed.js +++ /dev/null @@ -1,28 +0,0 @@ -/* eslint-disable no-console */ -const { PrismaClient } = require('@prisma/client') -const dotenv = require('dotenv') - -dotenv.config() -const db = new PrismaClient() - -async function main() { - // https://www.prisma.io/docs/guides/prisma-guides/seed-database - // - // Seed data is database data that needs to exist for your app to run. - // Ideally this file should be idempotent: running it multiple times - // will result in the same database state (usually by checking for the - // existence of a record before trying to create it). For example: - // - // const existing = await db.user.findMany({ where: { email: 'admin@email.com' }}) - // if (!existing.length) { - // await db.user.create({ data: { name: 'Admin', email: 'admin@email.com' }}) - // } - - console.info('No data to seed. See api/db/seed.js for info.') -} - -main() - .catch((e) => console.error(e)) - .finally(async () => { - await db.$disconnect() - }) diff --git a/framework-boilerplates/redwoodjs/api/jest.config.js b/framework-boilerplates/redwoodjs/api/jest.config.js index 09205b8691..932fc82dce 100644 --- a/framework-boilerplates/redwoodjs/api/jest.config.js +++ b/framework-boilerplates/redwoodjs/api/jest.config.js @@ -1,6 +1,8 @@ -const { getConfig } = require('@redwoodjs/core') +// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build -const config = getConfig({ type: 'jest', target: 'node' }) -config.displayName.name = 'api' +const config = { + rootDir: '../', + preset: '@redwoodjs/testing/config/jest/api', +} module.exports = config diff --git a/framework-boilerplates/redwoodjs/api/jsconfig.json b/framework-boilerplates/redwoodjs/api/jsconfig.json index a41e54d104..eaaafb7830 100644 --- a/framework-boilerplates/redwoodjs/api/jsconfig.json +++ b/framework-boilerplates/redwoodjs/api/jsconfig.json @@ -1,9 +1,41 @@ { "compilerOptions": { - "baseUrl": ".", + "noEmit": true, + "esModuleInterop": true, + "target": "ES2023", + "module": "Node16", + "moduleResolution": "Node16", + "skipLibCheck": false, + "rootDirs": [ + "./src", + "../.redwood/types/mirror/api/src" + ], "paths": { - "src/*": ["./src/*"] - } + "src/*": [ + "./src/*", + "../.redwood/types/mirror/api/src/*" + ], + "types/*": [ + "./types/*", + "../types/*" + ], + "@redwoodjs/testing": [ + "../node_modules/@redwoodjs/testing/api" + ] + }, + "typeRoots": [ + "../node_modules/@types", + "./node_modules/@types" + ], + "types": [ + "jest" + ], + "jsx": "react-jsx" }, - "include": ["src/**/*", "../.redwood/index.d.ts"] + "include": [ + "src", + "../.redwood/types/includes/all-*", + "../.redwood/types/includes/api-*", + "../types" + ] } diff --git a/framework-boilerplates/redwoodjs/api/package.json b/framework-boilerplates/redwoodjs/api/package.json index 2e5630a925..b5ed443f2c 100644 --- a/framework-boilerplates/redwoodjs/api/package.json +++ b/framework-boilerplates/redwoodjs/api/package.json @@ -3,6 +3,7 @@ "version": "0.0.0", "private": true, "dependencies": { - "@redwoodjs/api": "^0.25.0" + "@redwoodjs/api": "8.8.1", + "@redwoodjs/graphql-server": "8.8.1" } } diff --git a/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.js b/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.js new file mode 100644 index 0000000000..df6083b143 --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.js @@ -0,0 +1,22 @@ +import gql from 'graphql-tag' + +import { createValidatorDirective } from '@redwoodjs/graphql-server' + +import { requireAuth as applicationRequireAuth } from 'src/lib/auth' + +export const schema = gql` + """ + Use to check whether or not a user is authenticated and is associated + with an optional set of roles. + """ + directive @requireAuth(roles: [String]) on FIELD_DEFINITION +` + +const validate = ({ directiveArgs }) => { + const { roles } = directiveArgs + applicationRequireAuth({ roles }) +} + +const requireAuth = createValidatorDirective(schema, validate) + +export default requireAuth diff --git a/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.test.js b/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.test.js new file mode 100644 index 0000000000..0f01aa367a --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/directives/requireAuth/requireAuth.test.js @@ -0,0 +1,18 @@ +import { mockRedwoodDirective, getDirectiveName } from '@redwoodjs/testing/api' + +import requireAuth from './requireAuth' + +describe('requireAuth directive', () => { + it('declares the directive sdl as schema, with the correct name', () => { + expect(requireAuth.schema).toBeTruthy() + expect(getDirectiveName(requireAuth.schema)).toBe('requireAuth') + }) + + it('requireAuth has stub implementation. Should not throw when current user', () => { + // If you want to set values in context, pass it through e.g. + // mockRedwoodDirective(requireAuth, { context: { currentUser: { id: 1, name: 'Lebron McGretzky' } }}) + const mockExecution = mockRedwoodDirective(requireAuth, { context: {} }) + + expect(mockExecution).not.toThrowError() + }) +}) diff --git a/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.js b/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.js new file mode 100644 index 0000000000..e85b94ae8b --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.js @@ -0,0 +1,16 @@ +import gql from 'graphql-tag' + +import { createValidatorDirective } from '@redwoodjs/graphql-server' + +export const schema = gql` + """ + Use to skip authentication checks and allow public access. + """ + directive @skipAuth on FIELD_DEFINITION +` + +const skipAuth = createValidatorDirective(schema, () => { + return +}) + +export default skipAuth diff --git a/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.test.js b/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.test.js new file mode 100644 index 0000000000..88d99a56ea --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/directives/skipAuth/skipAuth.test.js @@ -0,0 +1,10 @@ +import { getDirectiveName } from '@redwoodjs/testing/api' + +import skipAuth from './skipAuth' + +describe('skipAuth directive', () => { + it('declares the directive sdl as schema, with the correct name', () => { + expect(skipAuth.schema).toBeTruthy() + expect(getDirectiveName(skipAuth.schema)).toBe('skipAuth') + }) +}) diff --git a/framework-boilerplates/redwoodjs/api/src/functions/graphql.js b/framework-boilerplates/redwoodjs/api/src/functions/graphql.js index 8ab7004fd7..f395c3b0f8 100644 --- a/framework-boilerplates/redwoodjs/api/src/functions/graphql.js +++ b/framework-boilerplates/redwoodjs/api/src/functions/graphql.js @@ -1,18 +1,17 @@ -import { - createGraphQLHandler, - makeMergedSchema, - makeServices, -} from '@redwoodjs/api' +import { createGraphQLHandler } from '@redwoodjs/graphql-server' -import schemas from 'src/graphql/**/*.{js,ts}' -import { db } from 'src/lib/db' +import directives from 'src/directives/**/*.{js,ts}' +import sdls from 'src/graphql/**/*.sdl.{js,ts}' import services from 'src/services/**/*.{js,ts}' +import { db } from 'src/lib/db' +import { logger } from 'src/lib/logger' + export const handler = createGraphQLHandler({ - schema: makeMergedSchema({ - schemas, - services: makeServices({ services }), - }), + loggerConfig: { logger, options: {} }, + directives, + sdls, + services, onException: () => { // Disconnect from your database with an unhandled exception. db.$disconnect() diff --git a/framework-boilerplates/redwoodjs/api/src/lib/auth.js b/framework-boilerplates/redwoodjs/api/src/lib/auth.js new file mode 100644 index 0000000000..c55e5f7535 --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/lib/auth.js @@ -0,0 +1,32 @@ +/** + * Once you are ready to add authentication to your application + * you'll build out requireAuth() with real functionality. For + * now we just return `true` so that the calls in services + * have something to check against, simulating a logged + * in user that is allowed to access that service. + * + * See https://redwoodjs.com/docs/authentication for more info. + */ +export const isAuthenticated = () => { + return true +} + +export const hasRole = ({ roles }) => { + return roles !== undefined +} + +// This is used by the redwood directive +// in ./api/src/directives/requireAuth + +// Roles are passed in by the requireAuth directive if you have auth setup +// eslint-disable-next-line no-unused-vars +export const requireAuth = ({ roles }) => { + return isAuthenticated() +} + +export const getCurrentUser = async () => { + throw new Error( + 'Auth is not set up yet. See https://redwoodjs.com/docs/authentication ' + + 'to get started' + ) +} diff --git a/framework-boilerplates/redwoodjs/api/src/lib/db.js b/framework-boilerplates/redwoodjs/api/src/lib/db.js index 465626a85b..006193cc6b 100644 --- a/framework-boilerplates/redwoodjs/api/src/lib/db.js +++ b/framework-boilerplates/redwoodjs/api/src/lib/db.js @@ -3,4 +3,24 @@ import { PrismaClient } from '@prisma/client' -export const db = new PrismaClient() +import { emitLogLevels, handlePrismaLogging } from '@redwoodjs/api/logger' + +import { logger } from './logger' + +const prismaClient = new PrismaClient({ + log: emitLogLevels(['info', 'warn', 'error']), +}) + +handlePrismaLogging({ + db: prismaClient, + logger, + logLevels: ['info', 'warn', 'error'], +}) + +/** + * Global Prisma client extensions should be added here, as $extend + * returns a new instance. + * export const db = prismaClient.$extend(...) + * Add any .$on hooks before using $extend + */ +export const db = prismaClient diff --git a/framework-boilerplates/redwoodjs/api/src/lib/logger.js b/framework-boilerplates/redwoodjs/api/src/lib/logger.js new file mode 100644 index 0000000000..150a309767 --- /dev/null +++ b/framework-boilerplates/redwoodjs/api/src/lib/logger.js @@ -0,0 +1,17 @@ +import { createLogger } from '@redwoodjs/api/logger' + +/** + * Creates a logger with RedwoodLoggerOptions + * + * These extend and override default LoggerOptions, + * can define a destination like a file or other supported pino log transport stream, + * and sets whether or not to show the logger configuration settings (defaults to false) + * + * @param RedwoodLoggerOptions + * + * RedwoodLoggerOptions have + * @param {options} LoggerOptions - defines how to log, such as redaction and format + * @param {string | DestinationStream} destination - defines where to log, such as a transport stream or file + * @param {boolean} showConfig - whether to display logger configuration on initialization + */ +export const logger = createLogger({}) diff --git a/framework-boilerplates/redwoodjs/babel.config.js b/framework-boilerplates/redwoodjs/babel.config.js deleted file mode 100644 index 0bb758f7df..0000000000 --- a/framework-boilerplates/redwoodjs/babel.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - presets: ['@redwoodjs/core/config/babel-preset'], -} diff --git a/framework-boilerplates/redwoodjs/graphql.config.js b/framework-boilerplates/redwoodjs/graphql.config.js index 802b5d1519..c564451acb 100644 --- a/framework-boilerplates/redwoodjs/graphql.config.js +++ b/framework-boilerplates/redwoodjs/graphql.config.js @@ -1,7 +1,11 @@ -const { getConfig } = require('@redwoodjs/internal') +// This file is used by the VSCode GraphQL extension -const config = getConfig() +const { getPaths } = require('@redwoodjs/project-config') -module.exports = { - schema: `http://${config.api.host}:${config.api.port}/graphql`, +/** @type {import('graphql-config').IGraphQLConfig} */ +const config = { + schema: getPaths().generated.schema, + documents: './web/src/**/!(*.d).{ts,tsx,js,jsx}', } + +module.exports = config diff --git a/framework-boilerplates/redwoodjs/jest.config.js b/framework-boilerplates/redwoodjs/jest.config.js new file mode 100644 index 0000000000..c6b395cb76 --- /dev/null +++ b/framework-boilerplates/redwoodjs/jest.config.js @@ -0,0 +1,8 @@ +// This the Redwood root jest config +// Each side, e.g. ./web/ and ./api/ has specific config that references this root +// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build + +module.exports = { + rootDir: '.', + projects: ['/{*,!(node_modules)/**/}/jest.config.js'], +} diff --git a/framework-boilerplates/redwoodjs/package.json b/framework-boilerplates/redwoodjs/package.json index d0905b237c..d8994ea558 100644 --- a/framework-boilerplates/redwoodjs/package.json +++ b/framework-boilerplates/redwoodjs/package.json @@ -3,18 +3,25 @@ "workspaces": { "packages": [ "api", - "web", - "packages/*" + "web" ] }, "devDependencies": { - "@redwoodjs/core": "^0.25.0" + "@redwoodjs/core": "8.8.1", + "@redwoodjs/project-config": "8.8.1" }, "eslintConfig": { - "extends": "@redwoodjs/eslint-config" + "extends": "@redwoodjs/eslint-config", + "root": true }, "engines": { - "node": "16.x", - "yarn": ">=1.15" + "node": ">=22.x" + }, + "prisma": { + "seed": "yarn rw exec seed" + }, + "packageManager": "yarn@4.6.0", + "resolutions": { + "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz" } } diff --git a/framework-boilerplates/redwoodjs/prettier.config.js b/framework-boilerplates/redwoodjs/prettier.config.js index 5187cd4fdc..45058f7aa2 100644 --- a/framework-boilerplates/redwoodjs/prettier.config.js +++ b/framework-boilerplates/redwoodjs/prettier.config.js @@ -1,4 +1,5 @@ // https://prettier.io/docs/en/options.html +/** @type {import('prettier').RequiredOptions} */ module.exports = { trailingComma: 'es5', bracketSpacing: true, @@ -8,9 +9,9 @@ module.exports = { arrowParens: 'always', overrides: [ { - files: 'Routes.js', + files: 'Routes.*', options: { - printWidth: 200, + printWidth: 999, }, }, ], diff --git a/framework-boilerplates/redwoodjs/redwood.toml b/framework-boilerplates/redwoodjs/redwood.toml index dc61aee9a8..909d296bf5 100644 --- a/framework-boilerplates/redwoodjs/redwood.toml +++ b/framework-boilerplates/redwoodjs/redwood.toml @@ -6,10 +6,16 @@ # https://redwoodjs.com/docs/app-configuration-redwood-toml [web] + title = "Redwood App" port = 8910 - apiProxyPath = "/api" + apiUrl = "/api" + includeEnvironmentVariables = [ + # Add any ENV vars that should be available to the web side to this array + # See https://redwoodjs.com/docs/environment-variables#web + ] [api] port = 8911 - schemaPath = "./api/db/schema.prisma" [browser] - open = false + open = true +[notifications] + versionUpdates = ["latest"] diff --git a/framework-boilerplates/redwoodjs/scripts/.keep b/framework-boilerplates/redwoodjs/scripts/.keep new file mode 100644 index 0000000000..e69de29bb2 diff --git a/framework-boilerplates/redwoodjs/scripts/jsconfig.json b/framework-boilerplates/redwoodjs/scripts/jsconfig.json new file mode 100644 index 0000000000..51ebe307ad --- /dev/null +++ b/framework-boilerplates/redwoodjs/scripts/jsconfig.json @@ -0,0 +1,54 @@ +{ + "compilerOptions": { + "noEmit": true, + "esModuleInterop": true, + "target": "ES2023", + "module": "Node16", + "moduleResolution": "Node16", + "paths": { + "$api/*": [ + "../api/*" + ], + "api/*": [ + "../api/*" + ], + "$api/src/*": [ + "../api/src/*", + "../.redwood/types/mirror/api/src/*" + ], + "api/src/*": [ + "../api/src/*", + "../.redwood/types/mirror/api/src/*" + ], + "$web/*": [ + "../web/*" + ], + "web/*": [ + "../web/*" + ], + "$web/src/*": [ + "../web/src/*", + "../.redwood/types/mirror/web/src/*" + ], + "web/src/*": [ + "../web/src/*", + "../.redwood/types/mirror/web/src/*" + ], + "types/*": [ + "../types/*", + "../web/types/*", + "../api/types/*" + ] + }, + "typeRoots": [ + "../node_modules/@types" + ], + "jsx": "preserve" + }, + "include": [ + ".", + "../.redwood/types/includes/all-*", + "../.redwood/types/includes/web-*", + "../types" + ] +} diff --git a/framework-boilerplates/redwoodjs/scripts/seed.js b/framework-boilerplates/redwoodjs/scripts/seed.js new file mode 100644 index 0000000000..5974285312 --- /dev/null +++ b/framework-boilerplates/redwoodjs/scripts/seed.js @@ -0,0 +1,27 @@ +// import { db } from 'api/src/lib/db' + +// Manually apply seeds via the `yarn rw prisma db seed` command. +// +// Seeds automatically run the first time you run the `yarn rw prisma migrate dev` +// command and every time you run the `yarn rw prisma migrate reset` command. +// +// See https://redwoodjs.com/docs/database-seeds for more info + +export default async () => { + try { + // Create your database records here! For example, seed some users: + // + // const users = [ + // { name: 'Alice', email: 'alice@redwoodjs.com' }, + // { name: 'Bob', email: 'bob@redwoodjs.com' }, + // ] + // + // await db.user.createMany({ data: users }) + + console.info( + '\n No seed data, skipping. See scripts/seed.js to start seeding your database!\n' + ) + } catch (error) { + console.error(error) + } +} diff --git a/framework-boilerplates/redwoodjs/vercel.json b/framework-boilerplates/redwoodjs/vercel.json new file mode 100644 index 0000000000..9c3a4a9f0e --- /dev/null +++ b/framework-boilerplates/redwoodjs/vercel.json @@ -0,0 +1,7 @@ +{ + "build": { + "env": { + "ENABLE_EXPERIMENTAL_COREPACK": "1" + } + } + } \ No newline at end of file diff --git a/framework-boilerplates/redwoodjs/web/.babelrc.js b/framework-boilerplates/redwoodjs/web/.babelrc.js deleted file mode 100644 index 73439347d4..0000000000 --- a/framework-boilerplates/redwoodjs/web/.babelrc.js +++ /dev/null @@ -1 +0,0 @@ -module.exports = { extends: '../babel.config.js' } diff --git a/framework-boilerplates/redwoodjs/web/jest.config.js b/framework-boilerplates/redwoodjs/web/jest.config.js index 29595385d5..0e54869ebd 100644 --- a/framework-boilerplates/redwoodjs/web/jest.config.js +++ b/framework-boilerplates/redwoodjs/web/jest.config.js @@ -1,6 +1,8 @@ -const { getConfig } = require('@redwoodjs/core') +// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build -const config = getConfig({ type: 'jest', target: 'browser' }) -config.displayName.name = 'web' +const config = { + rootDir: '../', + preset: '@redwoodjs/testing/config/jest/web', +} module.exports = config diff --git a/framework-boilerplates/redwoodjs/web/jsconfig.json b/framework-boilerplates/redwoodjs/web/jsconfig.json index 9da8404490..d8d5c5da88 100644 --- a/framework-boilerplates/redwoodjs/web/jsconfig.json +++ b/framework-boilerplates/redwoodjs/web/jsconfig.json @@ -1,10 +1,53 @@ { "compilerOptions": { - "baseUrl": ".", + "noEmit": true, + "esModuleInterop": true, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "skipLibCheck": false, + "rootDirs": [ + "./src", + "../.redwood/types/mirror/web/src", + "../api/src", + "../.redwood/types/mirror/api/src" + ], "paths": { - "src/*": ["./src/*"] + "src/*": [ + "./src/*", + "../.redwood/types/mirror/web/src/*", + "../api/src/*", + "../.redwood/types/mirror/api/src/*" + ], + "$api/*": [ + "../api/*" + ], + "types/*": [ + "./types/*", + "../types/*" + ], + "@redwoodjs/testing": [ + "../node_modules/@redwoodjs/testing/web" + ] }, + "typeRoots": [ + "../node_modules/@types", + "./node_modules/@types", + "../node_modules/@testing-library" + ], + "types": [ + "jest", + "jest-dom" + ], "jsx": "preserve" }, - "include": ["src/**/*", "../.redwood/index.d.ts"] + "include": [ + "src", + "config", + ".storybook/**/*", + "../.redwood/types/includes/all-*", + "../.redwood/types/includes/web-*", + "../types", + "./types" + ] } diff --git a/framework-boilerplates/redwoodjs/web/package.json b/framework-boilerplates/redwoodjs/web/package.json index ad807d3aa3..abf28dec69 100644 --- a/framework-boilerplates/redwoodjs/web/package.json +++ b/framework-boilerplates/redwoodjs/web/package.json @@ -7,17 +7,19 @@ "last 1 version" ], "production": [ - "defaults", - "not IE 11", - "not IE_Mob 11" + "defaults" ] }, "dependencies": { - "@redwoodjs/forms": "^0.25.0", - "@redwoodjs/router": "^0.25.0", - "@redwoodjs/web": "^0.25.0", - "prop-types": "^15.7.2", - "react": "^16.13.1", - "react-dom": "^16.13.1" + "@redwoodjs/forms": "8.8.1", + "@redwoodjs/router": "8.8.1", + "@redwoodjs/web": "8.8.1", + "react": "18.3.1", + "react-dom": "18.3.1" + }, + "devDependencies": { + "@redwoodjs/vite": "8.8.1", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19" } } diff --git a/framework-boilerplates/redwoodjs/web/public/README.md b/framework-boilerplates/redwoodjs/web/public/README.md index 896833e2db..1b09bf8361 100644 --- a/framework-boilerplates/redwoodjs/web/public/README.md +++ b/framework-boilerplates/redwoodjs/web/public/README.md @@ -1,6 +1,6 @@ # Static Assets -Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Webpack builds for production). They will also be available during development when you run `yarn rw dev`. +Use this folder to add static files directly to your app. All included files and folders will be copied directly into the `/dist` folder (created when Vite builds for production). They will also be available during development when you run `yarn rw dev`. > Note: files will _not_ hot reload while the development server is running. You'll need to manually stop/start to access file changes. @@ -18,15 +18,13 @@ and alt="Logo" /> ``` -Behind the scenes, we are using Webpack's ["copy-webpack-plugin"](https://github.com/webpack-contrib/copy-webpack-plugin). - ## Best Practices -Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Webpack, etc. +Because assets in this folder are bypassing the javascript module system, **this folder should be used sparingly** for assets such as favicons, robots.txt, manifests, libraries incompatible with Vite, etc. -In general, it's best to import files directly into a template, page, or component. This allows Webpack to include that file in the bundle, which ensures Webpack will correctly process and move assets into the distribution folder, providing error checks and correct paths along the way. +In general, it's best to import files directly into a template, page, or component. This allows Vite to include that file in the bundle when small enough, or to copy it over to the `dist` folder with a hash. -### Example Asset Import with Webpack +### Example Asset Import with Vite Instead of handling our logo image as a static file per the example above, we can do the following: @@ -42,4 +40,4 @@ function Header() { export default Header ``` -Behind the scenes, we are using Webpack's ["file-loader"](https://webpack.js.org/loaders/file-loader/) and ["url-loader](https://webpack.js.org/loaders/url-loader/) (for files smaller than 10kb). +See Vite's docs for [static asset handling](https://vitejs.dev/guide/assets.html) diff --git a/framework-boilerplates/redwoodjs/web/public/favicon.png b/framework-boilerplates/redwoodjs/web/public/favicon.png index f0cdd00c86..4741429417 100644 Binary files a/framework-boilerplates/redwoodjs/web/public/favicon.png and b/framework-boilerplates/redwoodjs/web/public/favicon.png differ diff --git a/framework-boilerplates/redwoodjs/web/src/App.jsx b/framework-boilerplates/redwoodjs/web/src/App.jsx new file mode 100644 index 0000000000..e570410a46 --- /dev/null +++ b/framework-boilerplates/redwoodjs/web/src/App.jsx @@ -0,0 +1,16 @@ +import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web' +import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' + +import FatalErrorPage from 'src/pages/FatalErrorPage' + +import './index.css' + +const App = ({ children }) => ( + + + {children} + + +) + +export default App diff --git a/framework-boilerplates/redwoodjs/web/src/Routes.js b/framework-boilerplates/redwoodjs/web/src/Routes.jsx similarity index 100% rename from framework-boilerplates/redwoodjs/web/src/Routes.js rename to framework-boilerplates/redwoodjs/web/src/Routes.jsx diff --git a/framework-boilerplates/redwoodjs/web/src/entry.client.jsx b/framework-boilerplates/redwoodjs/web/src/entry.client.jsx new file mode 100644 index 0000000000..915c14d76d --- /dev/null +++ b/framework-boilerplates/redwoodjs/web/src/entry.client.jsx @@ -0,0 +1,35 @@ +import { hydrateRoot, createRoot } from 'react-dom/client' + +import App from './App' +import Routes from './Routes' + +/** + * When `#redwood-app` isn't empty then it's very likely that you're using + * prerendering. So React attaches event listeners to the existing markup + * rather than replacing it. + * https://react.dev/reference/react-dom/client/hydrateRoot + */ +const redwoodAppElement = document.getElementById('redwood-app') + +if (!redwoodAppElement) { + throw new Error( + "Could not find an element with ID 'redwood-app'. Please ensure it " + + "exists in your 'web/src/index.html' file." + ) +} + +if (redwoodAppElement.children?.length > 0) { + hydrateRoot( + redwoodAppElement, + + + + ) +} else { + const root = createRoot(redwoodAppElement) + root.render( + + + + ) +} diff --git a/framework-boilerplates/redwoodjs/web/src/index.html b/framework-boilerplates/redwoodjs/web/src/index.html index 8f69230b33..e240b8eb8c 100644 --- a/framework-boilerplates/redwoodjs/web/src/index.html +++ b/framework-boilerplates/redwoodjs/web/src/index.html @@ -1,12 +1,15 @@ - - - - - <%= htmlWebpackPlugin.options.title %> - - -
- + + + + + + + + + +
+ + diff --git a/framework-boilerplates/redwoodjs/web/src/index.js b/framework-boilerplates/redwoodjs/web/src/index.js deleted file mode 100644 index 71679f7fd4..0000000000 --- a/framework-boilerplates/redwoodjs/web/src/index.js +++ /dev/null @@ -1,18 +0,0 @@ -import ReactDOM from 'react-dom' - -import { FatalErrorBoundary } from '@redwoodjs/web' -import { RedwoodApolloProvider } from '@redwoodjs/web/apollo' - -import FatalErrorPage from 'src/pages/FatalErrorPage' -import Routes from 'src/Routes' - -import './index.css' - -ReactDOM.render( - - - - - , - document.getElementById('redwood-app') -) diff --git a/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.js b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.jsx similarity index 95% rename from framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.js rename to framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.jsx index d20779a1a3..2c2d74e718 100644 --- a/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.js +++ b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.jsx @@ -40,7 +40,7 @@ const AboutPage = () => (

About

- Find me in ./web/src/pages/AboutPage/AboutPage.js + Find me in ./web/src/pages/AboutPage/AboutPage.jsx

My default route is named about, link to me with ` diff --git a/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.stories.jsx b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.stories.jsx new file mode 100644 index 0000000000..9220bcf4fe --- /dev/null +++ b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.stories.jsx @@ -0,0 +1,9 @@ +import AboutPage from './AboutPage' + +const meta = { + component: AboutPage, +} + +export default meta + +export const Primary = {} diff --git a/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.test.jsx b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.test.jsx new file mode 100644 index 0000000000..571b85e655 --- /dev/null +++ b/framework-boilerplates/redwoodjs/web/src/pages/AboutPage/AboutPage.test.jsx @@ -0,0 +1,14 @@ +import { render } from '@redwoodjs/testing/web' + +import AboutPage from './AboutPage' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-pages-layouts + +describe('AboutPage', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) +}) diff --git a/framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.js b/framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.jsx similarity index 76% rename from framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.js rename to framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.jsx index b21e3ccbdd..f7b2cc8719 100644 --- a/framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.js +++ b/framework-boilerplates/redwoodjs/web/src/pages/FatalErrorPage/FatalErrorPage.jsx @@ -5,13 +5,17 @@ // You can modify this page as you wish, but it is important to keep things simple to // avoid the possibility that it will cause its own error. If it does, Redwood will // still render a generic error page, but your users will prefer something a bit more -// thoughtful. =) +// thoughtful :) -export default () => ( -

-