From faa245fb5403121dd3520df3f0cbe0e5f6d948c1 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 6 May 2025 15:17:03 +0200 Subject: [PATCH 001/222] feat(crxa): Prep for ESM apps --- .../commands/generate/function/templates/function.ts.template | 2 +- .../cli/src/commands/generate/job/templates/job.ts.template | 2 +- .../cli/src/commands/generate/job/templates/test.ts.template | 2 +- .../templates/subscriptions/blank/blank.service.ts.template | 2 +- .../realtime/templates/subscriptions/blank/blank.ts.template | 2 +- .../generate/scaffold/templates/components/Name.tsx.template | 2 +- .../generate/scaffold/templates/components/Names.tsx.template | 2 +- .../scaffold/templates/lib/formatters.test.tsx.template | 2 +- .../cli/src/commands/setup/jobs/templates/jobs.ts.template | 4 ++-- .../templates/js/api/{jest.config.js => jest.config.cjs} | 0 .../js/api/src/directives/requireAuth/requireAuth.js | 2 +- .../js/api/src/directives/requireAuth/requireAuth.test.js | 2 +- .../templates/js/api/src/directives/skipAuth/skipAuth.test.js | 2 +- .../templates/js/api/src/functions/graphql.js | 4 ++-- packages/create-cedar-app/templates/js/api/src/lib/db.js | 2 +- .../templates/js/{graphql.config.js => graphql.config.cjs} | 0 .../templates/js/{jest.config.js => jest.config.cjs} | 0 .../templates/js/{prettier.config.js => prettier.config.cjs} | 0 .../templates/js/web/{jest.config.js => jest.config.cjs} | 0 .../templates/ts/api/{jest.config.js => jest.config.cjs} | 0 .../ts/api/src/directives/requireAuth/requireAuth.test.ts | 2 +- .../ts/api/src/directives/requireAuth/requireAuth.ts | 2 +- .../templates/ts/api/src/directives/skipAuth/skipAuth.test.ts | 2 +- .../templates/ts/api/src/functions/graphql.ts | 4 ++-- packages/create-cedar-app/templates/ts/api/src/lib/db.ts | 2 +- .../templates/ts/{graphql.config.js => graphql.config.cjs} | 0 .../templates/ts/{jest.config.js => jest.config.cjs} | 0 .../templates/ts/{prettier.config.js => prettier.config.cjs} | 0 .../templates/ts/web/{jest.config.js => jest.config.cjs} | 0 29 files changed, 22 insertions(+), 22 deletions(-) rename packages/create-cedar-app/templates/js/api/{jest.config.js => jest.config.cjs} (100%) rename packages/create-cedar-app/templates/js/{graphql.config.js => graphql.config.cjs} (100%) rename packages/create-cedar-app/templates/js/{jest.config.js => jest.config.cjs} (100%) rename packages/create-cedar-app/templates/js/{prettier.config.js => prettier.config.cjs} (100%) rename packages/create-cedar-app/templates/js/web/{jest.config.js => jest.config.cjs} (100%) rename packages/create-cedar-app/templates/ts/api/{jest.config.js => jest.config.cjs} (100%) rename packages/create-cedar-app/templates/ts/{graphql.config.js => graphql.config.cjs} (100%) rename packages/create-cedar-app/templates/ts/{jest.config.js => jest.config.cjs} (100%) rename packages/create-cedar-app/templates/ts/{prettier.config.js => prettier.config.cjs} (100%) rename packages/create-cedar-app/templates/ts/web/{jest.config.js => jest.config.cjs} (100%) diff --git a/packages/cli/src/commands/generate/function/templates/function.ts.template b/packages/cli/src/commands/generate/function/templates/function.ts.template index 8686ed1d0d..92681a7416 100644 --- a/packages/cli/src/commands/generate/function/templates/function.ts.template +++ b/packages/cli/src/commands/generate/function/templates/function.ts.template @@ -1,6 +1,6 @@ import type { APIGatewayEvent, Context } from 'aws-lambda' -import { logger } from 'src/lib/logger' +import { logger } from 'src/lib/logger.js' /** * The handler function is your code that processes http request events. diff --git a/packages/cli/src/commands/generate/job/templates/job.ts.template b/packages/cli/src/commands/generate/job/templates/job.ts.template index 8c049fb3fd..6d070251bf 100644 --- a/packages/cli/src/commands/generate/job/templates/job.ts.template +++ b/packages/cli/src/commands/generate/job/templates/job.ts.template @@ -1,4 +1,4 @@ -import { jobs } from 'src/lib/jobs' +import { jobs } from 'src/lib/jobs.js' export const ${name}Job = jobs.createJob({ queue: '${queueName}', diff --git a/packages/cli/src/commands/generate/job/templates/test.ts.template b/packages/cli/src/commands/generate/job/templates/test.ts.template index 9ff7a20db4..ad38bf6eaf 100644 --- a/packages/cli/src/commands/generate/job/templates/test.ts.template +++ b/packages/cli/src/commands/generate/job/templates/test.ts.template @@ -1,4 +1,4 @@ -import { ${name}Job } from './${name}Job' +import { ${name}Job } from './${name}Job.js' describe('${name}Job', () => { it('should not throw any errors', async () => { diff --git a/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template b/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template index f5269228d9..9515233901 100644 --- a/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template +++ b/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.service.ts.template @@ -1,6 +1,6 @@ import type { ${subscriptionInputType} } from 'types/graphql' -import { logger } from 'src/lib/logger' +import { logger } from 'src/lib/logger.js' import type { Publish${typeName}ChannelType } from 'src/subscriptions/${name}/${name}' export const ${subscriptionQueryName} = ({ id }) => [id] diff --git a/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template b/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template index 22a8126439..eaca0da4d1 100644 --- a/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template +++ b/packages/cli/src/commands/generate/realtime/templates/subscriptions/blank/blank.ts.template @@ -2,7 +2,7 @@ import gql from 'graphql-tag' import type { PubSub } from '@cedarjs/realtime' -import { logger } from 'src/lib/logger' +import { logger } from 'src/lib/logger.js' export const schema = gql` type Subscription { diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template index 4f37c81fac..6ec9b8d470 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Name.tsx.template @@ -5,7 +5,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { ${formattersImports} } from 'src/lib/formatters' +import { ${formattersImports} } from 'src/lib/formatters.js' const DELETE_${singularConstantName}_MUTATION: TypedDocumentNode = gql` mutation Delete${singularPascalName}Mutation($${idName}: ${idType}!) { diff --git a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template index f904afbe55..f4d3cdc015 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/components/Names.tsx.template @@ -6,7 +6,7 @@ import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' import { QUERY } from '${importComponentNamesCell}' -import { ${listFormattersImports} } from 'src/lib/formatters' +import { ${listFormattersImports} } from 'src/lib/formatters.js' const DELETE_${singularConstantName}_MUTATION: TypedDocumentNode = gql` diff --git a/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template index 75bc89ebea..0383c42716 100644 --- a/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template +++ b/packages/cli/src/commands/generate/scaffold/templates/lib/formatters.test.tsx.template @@ -7,7 +7,7 @@ import { timeTag, jsonDisplay, checkboxInputTag, -} from './formatters' +} from './formatters.js' describe('formatEnum', () => { it('handles nullish values', () => { diff --git a/packages/cli/src/commands/setup/jobs/templates/jobs.ts.template b/packages/cli/src/commands/setup/jobs/templates/jobs.ts.template index 76e7b0fd9a..0d8b65d0fd 100644 --- a/packages/cli/src/commands/setup/jobs/templates/jobs.ts.template +++ b/packages/cli/src/commands/setup/jobs/templates/jobs.ts.template @@ -4,8 +4,8 @@ import { PrismaAdapter, JobManager } from '@cedarjs/jobs' -import { db } from 'src/lib/db' -import { logger } from 'src/lib/logger' +import { db } from 'src/lib/db.js' +import { logger } from 'src/lib/logger.js' export const jobs = new JobManager({ adapters: { diff --git a/packages/create-cedar-app/templates/js/api/jest.config.js b/packages/create-cedar-app/templates/js/api/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/js/api/jest.config.js rename to packages/create-cedar-app/templates/js/api/jest.config.cjs diff --git a/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.js b/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.js index 3c6232de4b..78b77ce503 100644 --- a/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.js +++ b/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.js @@ -2,7 +2,7 @@ import gql from 'graphql-tag' import { createValidatorDirective } from '@cedarjs/graphql-server' -import { requireAuth as applicationRequireAuth } from 'src/lib/auth' +import { requireAuth as applicationRequireAuth } from 'src/lib/auth.js' export const schema = gql` """ diff --git a/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.test.js b/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.test.js index e236f86ed3..1de4ac9b65 100644 --- a/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.test.js +++ b/packages/create-cedar-app/templates/js/api/src/directives/requireAuth/requireAuth.test.js @@ -1,6 +1,6 @@ import { mockRedwoodDirective, getDirectiveName } from '@cedarjs/testing/api' -import requireAuth from './requireAuth' +import requireAuth from './requireAuth.js' describe('requireAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/packages/create-cedar-app/templates/js/api/src/directives/skipAuth/skipAuth.test.js b/packages/create-cedar-app/templates/js/api/src/directives/skipAuth/skipAuth.test.js index 65465cd5c8..68c006bdae 100644 --- a/packages/create-cedar-app/templates/js/api/src/directives/skipAuth/skipAuth.test.js +++ b/packages/create-cedar-app/templates/js/api/src/directives/skipAuth/skipAuth.test.js @@ -1,6 +1,6 @@ import { getDirectiveName } from '@cedarjs/testing/api' -import skipAuth from './skipAuth' +import skipAuth from './skipAuth.js' describe('skipAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/packages/create-cedar-app/templates/js/api/src/functions/graphql.js b/packages/create-cedar-app/templates/js/api/src/functions/graphql.js index f2605b3941..1cbf10059a 100644 --- a/packages/create-cedar-app/templates/js/api/src/functions/graphql.js +++ b/packages/create-cedar-app/templates/js/api/src/functions/graphql.js @@ -4,8 +4,8 @@ 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' +import { db } from 'src/lib/db.js' +import { logger } from 'src/lib/logger.js' export const handler = createGraphQLHandler({ loggerConfig: { logger, options: {} }, diff --git a/packages/create-cedar-app/templates/js/api/src/lib/db.js b/packages/create-cedar-app/templates/js/api/src/lib/db.js index 669eac042b..b6fa93228f 100644 --- a/packages/create-cedar-app/templates/js/api/src/lib/db.js +++ b/packages/create-cedar-app/templates/js/api/src/lib/db.js @@ -5,7 +5,7 @@ import { PrismaClient } from '@prisma/client' import { emitLogLevels, handlePrismaLogging } from '@cedarjs/api/logger' -import { logger } from './logger' +import { logger } from './logger.js' const prismaClient = new PrismaClient({ log: emitLogLevels(['info', 'warn', 'error']), diff --git a/packages/create-cedar-app/templates/js/graphql.config.js b/packages/create-cedar-app/templates/js/graphql.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/js/graphql.config.js rename to packages/create-cedar-app/templates/js/graphql.config.cjs diff --git a/packages/create-cedar-app/templates/js/jest.config.js b/packages/create-cedar-app/templates/js/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/js/jest.config.js rename to packages/create-cedar-app/templates/js/jest.config.cjs diff --git a/packages/create-cedar-app/templates/js/prettier.config.js b/packages/create-cedar-app/templates/js/prettier.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/js/prettier.config.js rename to packages/create-cedar-app/templates/js/prettier.config.cjs diff --git a/packages/create-cedar-app/templates/js/web/jest.config.js b/packages/create-cedar-app/templates/js/web/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/js/web/jest.config.js rename to packages/create-cedar-app/templates/js/web/jest.config.cjs diff --git a/packages/create-cedar-app/templates/ts/api/jest.config.js b/packages/create-cedar-app/templates/ts/api/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/ts/api/jest.config.js rename to packages/create-cedar-app/templates/ts/api/jest.config.cjs diff --git a/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.test.ts b/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.test.ts index e236f86ed3..1de4ac9b65 100644 --- a/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.test.ts +++ b/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.test.ts @@ -1,6 +1,6 @@ import { mockRedwoodDirective, getDirectiveName } from '@cedarjs/testing/api' -import requireAuth from './requireAuth' +import requireAuth from './requireAuth.js' describe('requireAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.ts b/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.ts index aa00bc0513..f42ba11d60 100644 --- a/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.ts +++ b/packages/create-cedar-app/templates/ts/api/src/directives/requireAuth/requireAuth.ts @@ -3,7 +3,7 @@ import gql from 'graphql-tag' import type { ValidatorDirectiveFunc } from '@cedarjs/graphql-server' import { createValidatorDirective } from '@cedarjs/graphql-server' -import { requireAuth as applicationRequireAuth } from 'src/lib/auth' +import { requireAuth as applicationRequireAuth } from 'src/lib/auth.js' export const schema = gql` """ diff --git a/packages/create-cedar-app/templates/ts/api/src/directives/skipAuth/skipAuth.test.ts b/packages/create-cedar-app/templates/ts/api/src/directives/skipAuth/skipAuth.test.ts index 65465cd5c8..68c006bdae 100644 --- a/packages/create-cedar-app/templates/ts/api/src/directives/skipAuth/skipAuth.test.ts +++ b/packages/create-cedar-app/templates/ts/api/src/directives/skipAuth/skipAuth.test.ts @@ -1,6 +1,6 @@ import { getDirectiveName } from '@cedarjs/testing/api' -import skipAuth from './skipAuth' +import skipAuth from './skipAuth.js' describe('skipAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/packages/create-cedar-app/templates/ts/api/src/functions/graphql.ts b/packages/create-cedar-app/templates/ts/api/src/functions/graphql.ts index f2605b3941..1cbf10059a 100644 --- a/packages/create-cedar-app/templates/ts/api/src/functions/graphql.ts +++ b/packages/create-cedar-app/templates/ts/api/src/functions/graphql.ts @@ -4,8 +4,8 @@ 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' +import { db } from 'src/lib/db.js' +import { logger } from 'src/lib/logger.js' export const handler = createGraphQLHandler({ loggerConfig: { logger, options: {} }, diff --git a/packages/create-cedar-app/templates/ts/api/src/lib/db.ts b/packages/create-cedar-app/templates/ts/api/src/lib/db.ts index 669eac042b..b6fa93228f 100644 --- a/packages/create-cedar-app/templates/ts/api/src/lib/db.ts +++ b/packages/create-cedar-app/templates/ts/api/src/lib/db.ts @@ -5,7 +5,7 @@ import { PrismaClient } from '@prisma/client' import { emitLogLevels, handlePrismaLogging } from '@cedarjs/api/logger' -import { logger } from './logger' +import { logger } from './logger.js' const prismaClient = new PrismaClient({ log: emitLogLevels(['info', 'warn', 'error']), diff --git a/packages/create-cedar-app/templates/ts/graphql.config.js b/packages/create-cedar-app/templates/ts/graphql.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/ts/graphql.config.js rename to packages/create-cedar-app/templates/ts/graphql.config.cjs diff --git a/packages/create-cedar-app/templates/ts/jest.config.js b/packages/create-cedar-app/templates/ts/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/ts/jest.config.js rename to packages/create-cedar-app/templates/ts/jest.config.cjs diff --git a/packages/create-cedar-app/templates/ts/prettier.config.js b/packages/create-cedar-app/templates/ts/prettier.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/ts/prettier.config.js rename to packages/create-cedar-app/templates/ts/prettier.config.cjs diff --git a/packages/create-cedar-app/templates/ts/web/jest.config.js b/packages/create-cedar-app/templates/ts/web/jest.config.cjs similarity index 100% rename from packages/create-cedar-app/templates/ts/web/jest.config.js rename to packages/create-cedar-app/templates/ts/web/jest.config.cjs From 6208a069f7071dda2645d36e916333abc1180154 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 6 May 2025 19:24:30 +0200 Subject: [PATCH 002/222] Fix dbAuth setup and TW setup --- .../auth-providers/dbAuth/setup/src/setupHandler.ts | 6 ++++++ packages/cli-helpers/src/auth/authTasks.ts | 10 +++++----- .../commands/setup/ui/libraries/tailwindcssHandler.js | 4 ++-- packages/create-cedar-app/templates/js/scripts/seed.js | 2 +- packages/create-cedar-app/templates/ts/scripts/seed.ts | 2 +- packages/project-config/src/paths.ts | 2 +- tasks/test-project/codemods/seed.js | 4 ++-- 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/packages/auth-providers/dbAuth/setup/src/setupHandler.ts b/packages/auth-providers/dbAuth/setup/src/setupHandler.ts index 3c949e87bc..69790daa1e 100644 --- a/packages/auth-providers/dbAuth/setup/src/setupHandler.ts +++ b/packages/auth-providers/dbAuth/setup/src/setupHandler.ts @@ -185,6 +185,10 @@ export const createAuthDecoderFunction = { const content = fs.readFileSync(graphqlPath, 'utf-8') + if (!content) { + throw new Error('Could not read ' + graphqlPath) + } + let newContent = content.replace( 'import { getCurrentUser } from', 'import { cookieName, getCurrentUser } from', @@ -205,6 +209,8 @@ export const createAuthDecoderFunction = { } if (!newContent.includes('import { cookieName')) { + console.error('unexpected content\n') + console.error(newContent) throw new Error('Failed to import cookieName') } diff --git a/packages/cli-helpers/src/auth/authTasks.ts b/packages/cli-helpers/src/auth/authTasks.ts index 14e988c7cb..f6117abcca 100644 --- a/packages/cli-helpers/src/auth/authTasks.ts +++ b/packages/cli-helpers/src/auth/authTasks.ts @@ -18,8 +18,8 @@ import { import { apiSideFiles, generateUniqueFileNames } from './authFiles.js' -const AUTH_PROVIDER_HOOK_IMPORT = `import { AuthProvider, useAuth } from './auth'` -const AUTH_HOOK_IMPORT = `import { useAuth } from './auth'` +const AUTH_PROVIDER_HOOK_IMPORT = `import { AuthProvider, useAuth } from './auth.js'` +const AUTH_HOOK_IMPORT = `import { useAuth } from './auth.js'` export const getWebAppPath = () => getPaths().web.app @@ -110,15 +110,15 @@ export const addApiConfig = ({ } const hasCurrentUserImport = - /(^import {.*?getCurrentUser(?!getCurrentUser).*?} from ['"]src\/lib\/auth['"])/s.test( + /(^import {.*?getCurrentUser(?!getCurrentUser).*?} from ['"]src\/lib\/auth(?:\.js)['"])/s.test( newContent, ) if (!hasCurrentUserImport) { // add import statement newContent = newContent.replace( - /^(import { db } from ['"]src\/lib\/db['"])$/m, - `import { getCurrentUser } from 'src/lib/auth'\n$1`, + /^(import { db } from ['"]src\/lib\/db(?:\.js)['"])$/m, + `import { getCurrentUser } from 'src/lib/auth.js'\n$1`, ) // add object to handler diff --git a/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js b/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js index df698595d4..0fbaebcb7e 100644 --- a/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js +++ b/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js @@ -375,7 +375,7 @@ export const handler = async ({ force, install }) => { task: async (_ctx) => { const prettierConfigPath = path.join( rwPaths.base, - 'prettier.config.js', + 'prettier.config.cjs', ) // Add tailwindcss ordering plugin to prettier const prettierConfig = fs.readFileSync(prettierConfigPath, 'utf-8') @@ -413,7 +413,7 @@ export const handler = async ({ force, install }) => { task: async (_ctx, task) => { const prettierConfigPath = path.join( rwPaths.base, - 'prettier.config.js', + 'prettier.config.cjs', ) // Add tailwindcss ordering plugin to prettier const prettierConfig = fs.readFileSync(prettierConfigPath, 'utf-8') diff --git a/packages/create-cedar-app/templates/js/scripts/seed.js b/packages/create-cedar-app/templates/js/scripts/seed.js index 4091bc7cfc..e070a052a3 100644 --- a/packages/create-cedar-app/templates/js/scripts/seed.js +++ b/packages/create-cedar-app/templates/js/scripts/seed.js @@ -1,4 +1,4 @@ -// import { db } from 'api/src/lib/db' +// import { db } from 'api/src/lib/db.js' // Manually apply seeds via the `yarn rw prisma db seed` command. // diff --git a/packages/create-cedar-app/templates/ts/scripts/seed.ts b/packages/create-cedar-app/templates/ts/scripts/seed.ts index 3434e65c61..080e6d1d5a 100644 --- a/packages/create-cedar-app/templates/ts/scripts/seed.ts +++ b/packages/create-cedar-app/templates/ts/scripts/seed.ts @@ -1,4 +1,4 @@ -// import { db } from 'api/src/lib/db' +// import { db } from 'api/src/lib/db.js' // Manually apply seeds via the `yarn rw prisma db seed` command. // diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 457cf3ad9a..65e20c98ac 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -156,7 +156,7 @@ export const getBaseDirFromFile = (file: string) => { */ export const resolveFile = ( filePath: string, - extensions: string[] = ['.js', '.tsx', '.ts', '.jsx', '.mjs', '.mts'], + extensions: string[] = ['.js', '.tsx', '.ts', '.jsx', '.mjs', '.mts', '.cjs'], ): string | null => { for (const extension of extensions) { const p = `${filePath}${extension}` diff --git a/tasks/test-project/codemods/seed.js b/tasks/test-project/codemods/seed.js index bdb1b917b1..6b93649e40 100644 --- a/tasks/test-project/codemods/seed.js +++ b/tasks/test-project/codemods/seed.js @@ -69,8 +69,8 @@ export default (file, api) => { // Uncomment the db import line newSource = newSource.replace( - "// import { db } from 'api/src/lib/db'", - "import { db } from 'api/src/lib/db'", + "// import { db } from 'api/src/lib/db.js'", + "import { db } from 'api/src/lib/db.js'", ) return newSource From 355e9ce1d9ab14eadb9763bb15b429beb7941fa1 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 11:00:19 +0200 Subject: [PATCH 003/222] cli-helpers: fix auth setup --- .../__snapshots__/authTasks.test.ts.snap | 24 +++++++++---------- packages/cli-helpers/src/auth/authTasks.ts | 4 ++-- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/cli-helpers/src/auth/__tests__/__snapshots__/authTasks.test.ts.snap b/packages/cli-helpers/src/auth/__tests__/__snapshots__/authTasks.test.ts.snap index 74cfb77495..051a51ded2 100644 --- a/packages/cli-helpers/src/auth/__tests__/__snapshots__/authTasks.test.ts.snap +++ b/packages/cli-helpers/src/auth/__tests__/__snapshots__/authTasks.test.ts.snap @@ -7,7 +7,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -39,7 +39,7 @@ exports[`authTasks > Components with props > Should add useAuth on the same line import { Router, Route } from '@cedarjs/router' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( @@ -69,7 +69,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -101,7 +101,7 @@ exports[`authTasks > Components with props > Should not add useAuth if one alrea import { Router, Route } from '@cedarjs/router' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( @@ -136,7 +136,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -173,7 +173,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -233,7 +233,7 @@ exports[`authTasks > Should update App.{jsx,tsx}, Routes.{jsx,tsx} and add auth. import { Router, Route } from '@cedarjs/router' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( @@ -254,7 +254,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -339,7 +339,7 @@ exports[`authTasks > Should update App.{jsx,tsx}, Routes.{jsx,tsx} and add auth. import { Router, Route } from '@cedarjs/router' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( @@ -363,7 +363,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -391,7 +391,7 @@ exports[`authTasks > Swapped out GraphQL client > Should add auth config when ap import FatalErrorPage from 'src/pages/FatalErrorPage' import Routes from 'src/Routes' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' @@ -423,7 +423,7 @@ import directives from 'src/directives/**/*.{js,ts}' import sdls from 'src/graphql/**/*.sdl.{js,ts}' import services from 'src/services/**/*.{js,ts}' -import { getCurrentUser } from 'src/lib/auth' +import { getCurrentUser } from 'src/lib/auth.js' import { db } from 'src/lib/db' import { logger } from 'src/lib/logger' diff --git a/packages/cli-helpers/src/auth/authTasks.ts b/packages/cli-helpers/src/auth/authTasks.ts index f6117abcca..70edd1e338 100644 --- a/packages/cli-helpers/src/auth/authTasks.ts +++ b/packages/cli-helpers/src/auth/authTasks.ts @@ -110,14 +110,14 @@ export const addApiConfig = ({ } const hasCurrentUserImport = - /(^import {.*?getCurrentUser(?!getCurrentUser).*?} from ['"]src\/lib\/auth(?:\.js)['"])/s.test( + /(^import {.*?getCurrentUser(?!getCurrentUser).*?} from ['"]src\/lib\/auth(?:\.js)?['"])/s.test( newContent, ) if (!hasCurrentUserImport) { // add import statement newContent = newContent.replace( - /^(import { db } from ['"]src\/lib\/db(?:\.js)['"])$/m, + /^(import { db } from ['"]src\/lib\/db(?:\.js)?['"])$/m, `import { getCurrentUser } from 'src/lib/auth.js'\n$1`, ) From c9ff53ea656def507ef7eb3cb007e836e36c0c7d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 11:08:43 +0200 Subject: [PATCH 004/222] cli: update function and tailwind tests --- .../__tests__/__snapshots__/function.test.ts.snap | 8 ++++---- .../src/commands/setup/ui/__tests__/tailwindcss.test.ts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/commands/generate/function/__tests__/__snapshots__/function.test.ts.snap b/packages/cli/src/commands/generate/function/__tests__/__snapshots__/function.test.ts.snap index 8d6c799a60..40f024e2d4 100644 --- a/packages/cli/src/commands/generate/function/__tests__/__snapshots__/function.test.ts.snap +++ b/packages/cli/src/commands/generate/function/__tests__/__snapshots__/function.test.ts.snap @@ -42,7 +42,7 @@ describe('foo function', () => { `; exports[`Single word default files > creates a single word function file 1`] = ` -"import { logger } from 'src/lib/logger' +"import { logger } from 'src/lib/logger.js' /** * The handler function is your code that processes http request events. @@ -77,7 +77,7 @@ export const handler = async (event, _context) => { `; exports[`creates a .js file if --javascript=true 1`] = ` -"import { logger } from 'src/lib/logger' +"import { logger } from 'src/lib/logger.js' /** * The handler function is your code that processes http request events. @@ -114,7 +114,7 @@ export const handler = async (event, _context) => { exports[`creates a .ts file if --typescript=true 1`] = ` "import type { APIGatewayEvent, Context } from 'aws-lambda' -import { logger } from 'src/lib/logger' +import { logger } from 'src/lib/logger.js' /** * The handler function is your code that processes http request events. @@ -147,7 +147,7 @@ export const handler = async (event: APIGatewayEvent, _context: Context) => { `; exports[`creates a multi word function file 1`] = ` -"import { logger } from 'src/lib/logger' +"import { logger } from 'src/lib/logger.js' /** * The handler function is your code that processes http request events. diff --git a/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts b/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts index 1a593e7ec2..858e68df53 100644 --- a/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts +++ b/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts @@ -315,7 +315,7 @@ function setupDefaultProjectStructure( vol.fromJSON( { 'redwood.toml': '', - 'prettier.config.js': '', + 'prettier.config.cjs': '', 'web/config/': null, 'web/src/index.css': '', '.vscode/settings.json': [ From 80607fc691590c639112d4cccd708253c48ab1c0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 11:18:51 +0200 Subject: [PATCH 005/222] cli: update job and scaffold tests --- .../job/__tests__/__snapshots__/job.test.ts.snap | 4 ++-- .../__tests__/__snapshots__/scaffold.test.js.snap | 12 ++++++------ .../__snapshots__/scaffoldNoNest.test.js.snap | 8 ++++---- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/commands/generate/job/__tests__/__snapshots__/job.test.ts.snap b/packages/cli/src/commands/generate/job/__tests__/__snapshots__/job.test.ts.snap index 61221084ad..dcede4b54e 100644 --- a/packages/cli/src/commands/generate/job/__tests__/__snapshots__/job.test.ts.snap +++ b/packages/cli/src/commands/generate/job/__tests__/__snapshots__/job.test.ts.snap @@ -13,7 +13,7 @@ export type StandardScenario = ScenarioData `; exports[`Single word default files > creates a single word function file > Test snapshot 1`] = ` -"import { SampleJob } from './SampleJob' +"import { SampleJob } from './SampleJob.js' describe('SampleJob', () => { it('should not throw any errors', async () => { @@ -24,7 +24,7 @@ describe('SampleJob', () => { `; exports[`Single word default files > creates a single word function file 1`] = ` -"import { jobs } from 'src/lib/jobs' +"import { jobs } from 'src/lib/jobs.js' export const SampleJob = jobs.createJob({ queue: 'default', diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap index 3151d2113b..d6d7c5c34a 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffold.test.js.snap @@ -675,7 +675,7 @@ import { timeTag, jsonDisplay, checkboxInputTag, -} from './formatters' +} from './formatters.js' describe('formatEnum', () => { it('handles nullish values', () => { @@ -1056,7 +1056,7 @@ import { useMutation } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1722,7 +1722,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formatters' +} from 'src/lib/formatters.js' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -2343,7 +2343,7 @@ import { timeTag, jsonDisplay, checkboxInputTag, -} from './formatters' +} from './formatters.js' describe('formatEnum', () => { it('handles nullish values', () => { @@ -2767,7 +2767,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, @@ -3573,7 +3573,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formatters' +} from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, diff --git a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap index 45f9c1b411..e3489ca44f 100644 --- a/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap +++ b/packages/cli/src/commands/generate/scaffold/__tests__/__snapshots__/scaffoldNoNest.test.js.snap @@ -423,7 +423,7 @@ import { useMutation } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1089,7 +1089,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formatters' +} from 'src/lib/formatters.js' const DELETE_POST_MUTATION = gql\` mutation DeletePostMutation($id: Int!) { @@ -1668,7 +1668,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters' +import { checkboxInputTag, jsonDisplay, timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, @@ -2474,7 +2474,7 @@ import { jsonTruncate, timeTag, truncate, -} from 'src/lib/formatters' +} from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, From c8b15f77c6f04295ad54443d21bfadf2538b112d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 11:21:57 +0200 Subject: [PATCH 006/222] ccra: update test snapshot --- .../create-cedar-app/tests/templates.test.ts | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/packages/create-cedar-app/tests/templates.test.ts b/packages/create-cedar-app/tests/templates.test.ts index 71de0d8c7f..5ba067f8f0 100644 --- a/packages/create-cedar-app/tests/templates.test.ts +++ b/packages/create-cedar-app/tests/templates.test.ts @@ -26,7 +26,7 @@ describe('TS template', () => { "/api", "/api/db", "/api/db/schema.prisma", - "/api/jest.config.js", + "/api/jest.config.cjs", "/api/package.json", "/api/src", "/api/src/directives", @@ -48,17 +48,17 @@ describe('TS template', () => { "/api/src/services/.keep", "/api/tsconfig.json", "/gitignore.template", - "/graphql.config.js", - "/jest.config.js", + "/graphql.config.cjs", + "/jest.config.cjs", "/package.json", - "/prettier.config.js", + "/prettier.config.cjs", "/redwood.toml", "/scripts", "/scripts/.keep", "/scripts/seed.ts", "/scripts/tsconfig.json", "/web", - "/web/jest.config.js", + "/web/jest.config.cjs", "/web/package.json", "/web/public", "/web/public/README.md", @@ -108,7 +108,7 @@ describe('JS template', () => { "/api", "/api/db", "/api/db/schema.prisma", - "/api/jest.config.js", + "/api/jest.config.cjs", "/api/jsconfig.json", "/api/package.json", "/api/src", @@ -130,17 +130,17 @@ describe('JS template', () => { "/api/src/services", "/api/src/services/.keep", "/gitignore.template", - "/graphql.config.js", - "/jest.config.js", + "/graphql.config.cjs", + "/jest.config.cjs", "/package.json", - "/prettier.config.js", + "/prettier.config.cjs", "/redwood.toml", "/scripts", "/scripts/.keep", "/scripts/jsconfig.json", "/scripts/seed.js", "/web", - "/web/jest.config.js", + "/web/jest.config.cjs", "/web/jsconfig.json", "/web/package.json", "/web/public", From 09447d957128c7595c87a31d97ba0e382208e46b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 15:24:46 +0200 Subject: [PATCH 007/222] Use esbuild for exec with a tempoarary file --- packages/cli/package.json | 1 + packages/cli/src/commands/execHandler.js | 63 +------- packages/cli/src/lib/exec.js | 193 +++++++++++++++++++++-- 3 files changed, 180 insertions(+), 77 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index f58d47dd18..fca3131c91 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -38,6 +38,7 @@ "@cedarjs/structure": "workspace:*", "@cedarjs/telemetry": "workspace:*", "@cedarjs/web-server": "workspace:*", + "esbuild": "0.25.0", "@listr2/prompt-adapter-enquirer": "2.0.12", "@opentelemetry/api": "1.8.0", "@opentelemetry/core": "1.22.0", diff --git a/packages/cli/src/commands/execHandler.js b/packages/cli/src/commands/execHandler.js index d96e02d366..ae0e5002a1 100644 --- a/packages/cli/src/commands/execHandler.js +++ b/packages/cli/src/commands/execHandler.js @@ -5,10 +5,6 @@ import { context } from '@opentelemetry/api' import { suppressTracing } from '@opentelemetry/core' import { Listr } from 'listr2' -import { - getWebSideDefaultBabelConfig, - registerApiSideBabelHook, -} from '@cedarjs/babel-config' import { recordTelemetryAttributes } from '@cedarjs/cli-helpers' import { findScripts } from '@cedarjs/internal/dist/files' @@ -87,64 +83,7 @@ export const handler = async (args) => { delete scriptArgs.s delete scriptArgs.silent - const { - overrides: _overrides, - plugins: webPlugins, - ...otherWebConfig - } = getWebSideDefaultBabelConfig() - - // Import babel config for running script - registerApiSideBabelHook({ - plugins: [ - [ - 'babel-plugin-module-resolver', - { - alias: { - $api: getPaths().api.base, - $web: getPaths().web.base, - api: getPaths().api.base, - web: getPaths().web.base, - }, - loglevel: 'silent', // to silence the unnecessary warnings - }, - 'exec-$side-module-resolver', - ], - ], - overrides: [ - { - test: ['./api/'], - plugins: [ - [ - 'babel-plugin-module-resolver', - { - alias: { - src: getPaths().api.src, - }, - loglevel: 'silent', - }, - 'exec-api-src-module-resolver', - ], - ], - }, - { - test: ['./web/'], - plugins: [ - ...webPlugins, - [ - 'babel-plugin-module-resolver', - { - alias: { - src: getPaths().web.src, - }, - loglevel: 'silent', - }, - 'exec-web-src-module-resolver', - ], - ], - ...otherWebConfig, - }, - ], - }) + // Configuration is now handled in the runScriptFunction const scriptPath = resolveScriptPath(name) diff --git a/packages/cli/src/lib/exec.js b/packages/cli/src/lib/exec.js index 958bdb06eb..24ee613198 100644 --- a/packages/cli/src/lib/exec.js +++ b/packages/cli/src/lib/exec.js @@ -1,5 +1,7 @@ -import { createRequire } from 'node:module' -import path from 'node:path' +import fs from 'fs' +import path from 'path' + +import * as esbuild from 'esbuild' import { getWebSideDefaultBabelConfig, @@ -9,24 +11,185 @@ import { getPaths } from '@cedarjs/project-config' export async function runScriptFunction({ path: scriptPath, - functionName, + functionName = 'default', args, }) { - const createdRequire = createRequire(import.meta.url) - const script = createdRequire(scriptPath) - const returnValue = await script[functionName](args) - + // Configure babel with the same settings as before + configureBabel() + + // Read the source file + const source = fs.readFileSync(scriptPath, 'utf-8') + const fileExtension = path.extname(scriptPath) + + // Get paths configuration + const projectRoot = path.dirname(getPaths().base) + const paths = getPaths() + + // Use a temporary file with .mjs extension to ensure ESM compatibility + const tempDir = path.dirname(scriptPath) + const tempFilename = `__temp_${Date.now()}.mjs` + const tempFilePath = path.join(tempDir, tempFilename) + try { - const { db } = createdRequire(path.join(getPaths().api.lib, 'db')) - db.$disconnect() - } catch (e) { - // silence + // Use esbuild to bundle the script + await esbuild.build({ + stdin: { + contents: source, + loader: fileExtension.replace('.', ''), + resolveDir: path.dirname(scriptPath), + sourcefile: scriptPath, + }, + bundle: true, + format: 'esm', + platform: 'node', + target: 'node16', + absWorkingDir: projectRoot, + outfile: tempFilePath, + external: ['*'], + sourcemap: 'inline', + logLevel: 'error', + plugins: [ + { + name: 'redwood-path-resolver', + setup(build) { + // Custom resolver for redwood-specific imports + build.onResolve({ filter: /^(api|web)(\/|$)/ }, args => { + const match = args.path.match(/^(api|web)(\/|$)/) + if (!match) { + return null + } + + const side = match[1] // 'api' or 'web' + const restOfPath = args.path.slice(side.length + (match[2] === '/' ? 1 : 0)) + + // Convert .js extension to .ts if needed (or other appropriate extensions) + let fullPath = path.join( + side === 'api' ? paths.api.base : paths.web.base, + restOfPath + ) + + // If the path has a .js extension, check if a corresponding .ts file exists + if (path.extname(fullPath) === '.js') { + const tsPath = fullPath.replace(/\.js$/, '.ts') + const tsxPath = fullPath.replace(/\.js$/, '.tsx') + + if (fs.existsSync(tsPath)) { + return { path: tsPath } + } else if (fs.existsSync(tsxPath)) { + return { path: tsxPath } + } + } + + return { path: fullPath } + }) + + // Handle local imports with .js extension + build.onResolve({ filter: /\.js$/ }, args => { + // Skip node_modules + if (args.path.includes('node_modules')) { + return null + } + + // Handle relative imports + if (args.path.startsWith('.')) { + const resolvedPath = path.resolve(args.resolveDir, args.path) + const tsPath = resolvedPath.replace(/\.js$/, '.ts') + const tsxPath = resolvedPath.replace(/\.js$/, '.tsx') + + if (fs.existsSync(tsPath)) { + return { path: tsPath } + } else if (fs.existsSync(tsxPath)) { + return { path: tsxPath } + } + } + + return null + }) + } + } + ], + resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], + }) + + // Import the transpiled module (using dynamic import for ESM compatibility) + const module = await import(`file://${path.resolve(tempFilePath)}`) + + // Get the exported function (handle both named and default exports) + let exportedFunction + if (typeof module[functionName] === 'function') { + exportedFunction = module[functionName] + } else if (functionName === 'default' && typeof module.default === 'function') { + exportedFunction = module.default + } else { + throw new Error(`Function '${functionName}' not found in '${scriptPath}'`) + } + + // Execute the function + const returnValue = await exportedFunction(args) + + // Clean up database connections if they exist + try { + // Try to close db connection via dynamic ESM import first + try { + // Try a few potential paths for db + const dbPathOptions = [ + path.join(paths.api.lib, 'db'), + path.join(paths.api.src, 'lib', 'db') + ] + + for (const dbPath of dbPathOptions) { + try { + // Attempt to dynamically import the db module + const dbModule = await import(`file://${path.resolve(dbPath)}`) + + if (dbModule.db && typeof dbModule.db.$disconnect === 'function') { + await dbModule.db.$disconnect() + break + } else if (dbModule.default?.db && typeof dbModule.default.db.$disconnect === 'function') { + await dbModule.default.db.$disconnect() + break + } + } catch (e) { + // Continue to next path + } + } + } catch (e) { + // Silent failure for DB disconnect + } + } catch (e) { + // Silently handle database disconnection errors + } + + return returnValue + } catch (error) { + console.error(`Error details: ${error.message}`) + if (error.message.includes('Could not resolve')) { + throw new Error(`Error bundling '${scriptPath}': ${error.message}. Make sure all imports are available.`) + } else if (error.message.includes('Cannot find module')) { + throw new Error(`Error executing '${scriptPath}': ${error.message}. Check if the TypeScript file exists with a .ts extension when importing .js.`) + } else if (error.message.includes('Cannot read file')) { + throw new Error(`Error executing '${scriptPath}': ${error.message}. Check if TypeScript file exists at the correct location.`) + } else { + throw new Error(`Error executing '${scriptPath}': ${error.message}`) + } + } finally { + // Clean up the temporary file + try { + // Give a small delay to ensure file is not in use + await new Promise(resolve => setTimeout(resolve, 200)) + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath) + } else { + console.warn(`Temp file not found during cleanup: ${tempFilePath}`) + } + } catch (cleanupError) { + // Silently handle cleanup errors + console.warn(`Warning: Could not clean up temporary file: ${tempFilePath}`) + } } - - return returnValue } -export async function configureBabel() { +export function configureBabel() { const { overrides: _overrides, plugins: webPlugins, @@ -85,4 +248,4 @@ export async function configureBabel() { }, ], }) -} +} \ No newline at end of file From 953efe011b8e464611b9abd629fe9822b1bcf3bc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 18:05:42 +0200 Subject: [PATCH 008/222] exec: esbuild + babel --- packages/cli/package.json | 4 +- packages/cli/src/lib/exec.js | 209 ++++++++++++++++++++++++----------- yarn.lock | 2 + 3 files changed, 149 insertions(+), 66 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index fca3131c91..f8ee7c7903 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -28,6 +28,8 @@ "test:watch": "vitest watch" }, "dependencies": { + "@babel/core": "^7.26.10", + "@babel/preset-typescript": "7.27.1", "@babel/runtime-corejs3": "7.27.1", "@cedarjs/api-server": "workspace:*", "@cedarjs/cli-helpers": "workspace:*", @@ -38,7 +40,6 @@ "@cedarjs/structure": "workspace:*", "@cedarjs/telemetry": "workspace:*", "@cedarjs/web-server": "workspace:*", - "esbuild": "0.25.0", "@listr2/prompt-adapter-enquirer": "2.0.12", "@opentelemetry/api": "1.8.0", "@opentelemetry/core": "1.22.0", @@ -61,6 +62,7 @@ "dotenv-defaults": "5.0.2", "enquirer": "2.4.1", "envinfo": "7.14.0", + "esbuild": "0.25.0", "execa": "5.1.1", "fast-glob": "3.3.2", "fs-extra": "11.2.0", diff --git a/packages/cli/src/lib/exec.js b/packages/cli/src/lib/exec.js index 24ee613198..6c1a032c2f 100644 --- a/packages/cli/src/lib/exec.js +++ b/packages/cli/src/lib/exec.js @@ -1,6 +1,8 @@ import fs from 'fs' +import { createRequire } from 'module' import path from 'path' +import * as babel from '@babel/core' import * as esbuild from 'esbuild' import { @@ -14,22 +16,25 @@ export async function runScriptFunction({ functionName = 'default', args, }) { - // Configure babel with the same settings as before - configureBabel() - // Read the source file const source = fs.readFileSync(scriptPath, 'utf-8') const fileExtension = path.extname(scriptPath) - + + // Configure babel - we'll use its config but not require hooks + const babelConfig = getBabelConfig() + + // For database cleanup and module loading with CommonJS compatibility + const require = createRequire(import.meta.url) + // Get paths configuration const projectRoot = path.dirname(getPaths().base) const paths = getPaths() - + // Use a temporary file with .mjs extension to ensure ESM compatibility const tempDir = path.dirname(scriptPath) const tempFilename = `__temp_${Date.now()}.mjs` const tempFilePath = path.join(tempDir, tempFilename) - + try { // Use esbuild to bundle the script await esbuild.build({ @@ -49,126 +54,178 @@ export async function runScriptFunction({ sourcemap: 'inline', logLevel: 'error', plugins: [ + // Add babel plugin to run babel on JS/TS files before esbuild processes them + { + name: 'babel-transform', + setup(build) { + build.onLoad({ filter: /\.(jsx?|tsx?)$/ }, async (args) => { + // Read the source file + const source = await fs.promises.readFile(args.path, 'utf8') + + // Transform with babel first to apply all RW babel plugins + const result = await babel.transformAsync(source, { + ...babelConfig, + filename: args.path, + sourceMaps: 'inline', + }) + + if (!result || !result.code) { + return { contents: source } + } + + return { + contents: result.code, + loader: path.extname(args.path).replace('.', ''), + } + }) + }, + }, { name: 'redwood-path-resolver', setup(build) { // Custom resolver for redwood-specific imports - build.onResolve({ filter: /^(api|web)(\/|$)/ }, args => { + build.onResolve({ filter: /^(api|web)(\/|$)/ }, (args) => { const match = args.path.match(/^(api|web)(\/|$)/) if (!match) { return null } - + const side = match[1] // 'api' or 'web' - const restOfPath = args.path.slice(side.length + (match[2] === '/' ? 1 : 0)) - + const restOfPath = args.path.slice( + side.length + (match[2] === '/' ? 1 : 0), + ) + // Convert .js extension to .ts if needed (or other appropriate extensions) let fullPath = path.join( - side === 'api' ? paths.api.base : paths.web.base, - restOfPath + side === 'api' ? paths.api.base : paths.web.base, + restOfPath, ) - + // If the path has a .js extension, check if a corresponding .ts file exists + // If a .ts file exists, it returns that path instead of the .js path + // It also tries `.tsx` as a fallback if (path.extname(fullPath) === '.js') { const tsPath = fullPath.replace(/\.js$/, '.ts') const tsxPath = fullPath.replace(/\.js$/, '.tsx') - + if (fs.existsSync(tsPath)) { return { path: tsPath } } else if (fs.existsSync(tsxPath)) { return { path: tsxPath } } } - + return { path: fullPath } }) - + // Handle local imports with .js extension - build.onResolve({ filter: /\.js$/ }, args => { + build.onResolve({ filter: /\.js$/ }, (args) => { // Skip node_modules if (args.path.includes('node_modules')) { return null } - + // Handle relative imports if (args.path.startsWith('.')) { const resolvedPath = path.resolve(args.resolveDir, args.path) const tsPath = resolvedPath.replace(/\.js$/, '.ts') const tsxPath = resolvedPath.replace(/\.js$/, '.tsx') - + if (fs.existsSync(tsPath)) { return { path: tsPath } } else if (fs.existsSync(tsxPath)) { return { path: tsxPath } } } - + return null }) - } - } + }, + }, ], resolveExtensions: ['.ts', '.tsx', '.js', '.jsx', '.json'], }) - + // Import the transpiled module (using dynamic import for ESM compatibility) - const module = await import(`file://${path.resolve(tempFilePath)}`) - + const importedModule = await import(`file://${path.resolve(tempFilePath)}`) + // Get the exported function (handle both named and default exports) let exportedFunction - if (typeof module[functionName] === 'function') { - exportedFunction = module[functionName] - } else if (functionName === 'default' && typeof module.default === 'function') { - exportedFunction = module.default + if (typeof importedModule[functionName] === 'function') { + exportedFunction = importedModule[functionName] + } else if ( + functionName === 'default' && + typeof importedModule.default === 'function' + ) { + exportedFunction = importedModule.default } else { throw new Error(`Function '${functionName}' not found in '${scriptPath}'`) } - + // Execute the function const returnValue = await exportedFunction(args) - + // Clean up database connections if they exist try { - // Try to close db connection via dynamic ESM import first - try { - // Try a few potential paths for db - const dbPathOptions = [ - path.join(paths.api.lib, 'db'), - path.join(paths.api.src, 'lib', 'db') - ] - - for (const dbPath of dbPathOptions) { - try { - // Attempt to dynamically import the db module - const dbModule = await import(`file://${path.resolve(dbPath)}`) - - if (dbModule.db && typeof dbModule.db.$disconnect === 'function') { - await dbModule.db.$disconnect() - break - } else if (dbModule.default?.db && typeof dbModule.default.db.$disconnect === 'function') { - await dbModule.default.db.$disconnect() - break + // Try a few potential paths for db + const dbPathOptions = [ + path.join(paths.api.lib, 'db.js'), + path.join(paths.api.lib, 'db'), + ] + + // Try each path until we find one that works + for (const dbPath of dbPathOptions) { + try { + // Check if the module can be resolved + if (require.resolve(dbPath)) { + try { + const dbModule = require(dbPath) + if ( + dbModule.db && + typeof dbModule.db.$disconnect === 'function' + ) { + await dbModule.db.$disconnect() + break + } else if ( + dbModule.prisma && + typeof dbModule.prisma.$disconnect === 'function' + ) { + await dbModule.prisma.$disconnect() + break + } else if ( + dbModule.default?.db && + typeof dbModule.default.db.$disconnect === 'function' + ) { + await dbModule.default.db.$disconnect() + break + } + } catch (e) { + // Continue to next path } - } catch (e) { - // Continue to next path } + } catch (e) { + // Continue if require.resolve fails } - } catch (e) { - // Silent failure for DB disconnect } } catch (e) { - // Silently handle database disconnection errors + // Silent failure for DB disconnect } - + return returnValue } catch (error) { console.error(`Error details: ${error.message}`) if (error.message.includes('Could not resolve')) { - throw new Error(`Error bundling '${scriptPath}': ${error.message}. Make sure all imports are available.`) + throw new Error( + `Error bundling '${scriptPath}': ${error.message}. Make sure all imports are available.`, + ) } else if (error.message.includes('Cannot find module')) { - throw new Error(`Error executing '${scriptPath}': ${error.message}. Check if the TypeScript file exists with a .ts extension when importing .js.`) + throw new Error( + `Error executing '${scriptPath}': ${error.message}. Check if the TypeScript file exists with a .ts extension when importing .js.`, + ) } else if (error.message.includes('Cannot read file')) { - throw new Error(`Error executing '${scriptPath}': ${error.message}. Check if TypeScript file exists at the correct location.`) + throw new Error( + `Error executing '${scriptPath}': ${error.message}. Check if TypeScript file exists at the correct location.`, + ) } else { throw new Error(`Error executing '${scriptPath}': ${error.message}`) } @@ -176,7 +233,7 @@ export async function runScriptFunction({ // Clean up the temporary file try { // Give a small delay to ensure file is not in use - await new Promise(resolve => setTimeout(resolve, 200)) + await new Promise((resolve) => setTimeout(resolve, 100)) if (fs.existsSync(tempFilePath)) { fs.unlinkSync(tempFilePath) } else { @@ -184,20 +241,32 @@ export async function runScriptFunction({ } } catch (cleanupError) { // Silently handle cleanup errors - console.warn(`Warning: Could not clean up temporary file: ${tempFilePath}`) + console.warn( + `Warning: Could not clean up temporary file: ${tempFilePath}`, + ) } } } -export function configureBabel() { +// Get the Babel configuration without registering require hooks +export function getBabelConfig() { const { overrides: _overrides, plugins: webPlugins, ...otherWebConfig } = getWebSideDefaultBabelConfig() - // Import babel config for running script - registerApiSideBabelHook({ + // Create babel config for running script, similar to registerApiSideBabelHook + return { + presets: [ + [ + '@babel/preset-typescript', + { + isTSX: true, + allExtensions: true, + }, + ], + ], plugins: [ [ 'babel-plugin-module-resolver', @@ -247,5 +316,15 @@ export function configureBabel() { ...otherWebConfig, }, ], + babelrc: false, + configFile: false, + } +} + +// Legacy function for compatibility +export function configureBabel() { + registerApiSideBabelHook({ + plugins: getBabelConfig().plugins, + overrides: getBabelConfig().overrides, }) -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 9f011b6f26..8b2d888c19 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2723,6 +2723,7 @@ __metadata: dependencies: "@babel/cli": "npm:7.27.2" "@babel/core": "npm:^7.26.10" + "@babel/preset-typescript": "npm:7.27.1" "@babel/runtime-corejs3": "npm:7.27.1" "@cedarjs/api-server": "workspace:*" "@cedarjs/cli-helpers": "workspace:*" @@ -2756,6 +2757,7 @@ __metadata: dotenv-defaults: "npm:5.0.2" enquirer: "npm:2.4.1" envinfo: "npm:7.14.0" + esbuild: "npm:0.25.0" execa: "npm:5.1.1" fast-glob: "npm:3.3.2" fs-extra: "npm:11.2.0" From 514f6e9cb8bed17f3f5fcc2201c16c60a62f5b23 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 18:52:06 +0200 Subject: [PATCH 009/222] Update test project --- __fixtures__/test-project/api/jest.config.js | 8 --- .../requireAuth/requireAuth.test.ts | 2 +- .../src/directives/requireAuth/requireAuth.ts | 2 +- .../src/directives/skipAuth/skipAuth.test.ts | 2 +- .../test-project/api/src/functions/graphql.ts | 6 +-- __fixtures__/test-project/api/src/lib/db.ts | 2 +- .../src/services/contacts/contacts.test.ts | 4 +- __fixtures__/test-project/graphql.config.js | 11 ---- __fixtures__/test-project/jest.config.js | 8 --- __fixtures__/test-project/prettier.config.js | 20 -------- __fixtures__/test-project/scripts/seed.ts | 2 +- __fixtures__/test-project/web/jest.config.js | 8 --- __fixtures__/test-project/web/src/App.tsx | 2 +- __fixtures__/test-project/web/src/Routes.tsx | 2 +- .../components/Contact/Contact/Contact.tsx | 2 +- .../components/Contact/Contacts/Contacts.tsx | 2 +- .../web/src/components/Post/Post/Post.tsx | 2 +- .../web/src/components/Post/Posts/Posts.tsx | 2 +- .../web/src/lib/formatters.test.tsx | 2 +- .../src/pages/BlogPostPage/BlogPostPage.tsx | 1 - .../src/pages/WaterfallPage/WaterfallPage.tsx | 1 - .../test-project/web/src/scaffold.css | 50 +++++++++---------- .../appGqlConfigTransform.test.ts | 2 +- 23 files changed, 44 insertions(+), 99 deletions(-) delete mode 100644 __fixtures__/test-project/api/jest.config.js delete mode 100644 __fixtures__/test-project/graphql.config.js delete mode 100644 __fixtures__/test-project/jest.config.js delete mode 100644 __fixtures__/test-project/prettier.config.js delete mode 100644 __fixtures__/test-project/web/jest.config.js diff --git a/__fixtures__/test-project/api/jest.config.js b/__fixtures__/test-project/api/jest.config.js deleted file mode 100644 index 6646ef5637..0000000000 --- a/__fixtures__/test-project/api/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', - preset: '@cedarjs/testing/config/jest/api', -} - -module.exports = config diff --git a/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.test.ts b/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.test.ts index dd552cafa7..83b683768b 100644 --- a/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.test.ts +++ b/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.test.ts @@ -1,6 +1,6 @@ import { mockRedwoodDirective, getDirectiveName } from '@cedarjs/testing/api' -import requireAuth from './requireAuth' +import requireAuth from './requireAuth.js' describe('requireAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.ts b/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.ts index aa00bc0513..f42ba11d60 100644 --- a/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.ts +++ b/__fixtures__/test-project/api/src/directives/requireAuth/requireAuth.ts @@ -3,7 +3,7 @@ import gql from 'graphql-tag' import type { ValidatorDirectiveFunc } from '@cedarjs/graphql-server' import { createValidatorDirective } from '@cedarjs/graphql-server' -import { requireAuth as applicationRequireAuth } from 'src/lib/auth' +import { requireAuth as applicationRequireAuth } from 'src/lib/auth.js' export const schema = gql` """ diff --git a/__fixtures__/test-project/api/src/directives/skipAuth/skipAuth.test.ts b/__fixtures__/test-project/api/src/directives/skipAuth/skipAuth.test.ts index 65465cd5c8..68c006bdae 100644 --- a/__fixtures__/test-project/api/src/directives/skipAuth/skipAuth.test.ts +++ b/__fixtures__/test-project/api/src/directives/skipAuth/skipAuth.test.ts @@ -1,6 +1,6 @@ import { getDirectiveName } from '@cedarjs/testing/api' -import skipAuth from './skipAuth' +import skipAuth from './skipAuth.js' describe('skipAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/__fixtures__/test-project/api/src/functions/graphql.ts b/__fixtures__/test-project/api/src/functions/graphql.ts index 7b1d7d542a..4445ef3976 100644 --- a/__fixtures__/test-project/api/src/functions/graphql.ts +++ b/__fixtures__/test-project/api/src/functions/graphql.ts @@ -5,9 +5,9 @@ import directives from 'src/directives/**/*.{js,ts}' import sdls from 'src/graphql/**/*.sdl.{js,ts}' import services from 'src/services/**/*.{js,ts}' -import { cookieName, getCurrentUser } from 'src/lib/auth' -import { db } from 'src/lib/db' -import { logger } from 'src/lib/logger' +import { cookieName, getCurrentUser } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' +import { logger } from 'src/lib/logger.js' const authDecoder = createAuthDecoder(cookieName) diff --git a/__fixtures__/test-project/api/src/lib/db.ts b/__fixtures__/test-project/api/src/lib/db.ts index 669eac042b..b6fa93228f 100644 --- a/__fixtures__/test-project/api/src/lib/db.ts +++ b/__fixtures__/test-project/api/src/lib/db.ts @@ -5,7 +5,7 @@ import { PrismaClient } from '@prisma/client' import { emitLogLevels, handlePrismaLogging } from '@cedarjs/api/logger' -import { logger } from './logger' +import { logger } from './logger.js' const prismaClient = new PrismaClient({ log: emitLogLevels(['info', 'warn', 'error']), diff --git a/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts b/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts index 8bb328e175..bf017e4606 100644 --- a/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts +++ b/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts @@ -39,7 +39,9 @@ describe('contacts', () => { }) scenario('updates a contact', async (scenario: StandardScenario) => { - const original = (await contact({ id: scenario.contact.one.id })) as Contact + const original = (await contact({ + id: scenario.contact.one.id, + })) as Contact const result = await updateContact({ id: original.id, input: { name: 'String2' }, diff --git a/__fixtures__/test-project/graphql.config.js b/__fixtures__/test-project/graphql.config.js deleted file mode 100644 index d82bff34e0..0000000000 --- a/__fixtures__/test-project/graphql.config.js +++ /dev/null @@ -1,11 +0,0 @@ -// This file is used by the VSCode GraphQL extension - -const { getPaths } = require('@cedarjs/project-config') - -/** @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/__fixtures__/test-project/jest.config.js b/__fixtures__/test-project/jest.config.js deleted file mode 100644 index c6b395cb76..0000000000 --- a/__fixtures__/test-project/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// 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/__fixtures__/test-project/prettier.config.js b/__fixtures__/test-project/prettier.config.js deleted file mode 100644 index 3ed0f1e84d..0000000000 --- a/__fixtures__/test-project/prettier.config.js +++ /dev/null @@ -1,20 +0,0 @@ -// https://prettier.io/docs/en/options.html -/** @type {import('prettier').RequiredOptions} */ -module.exports = { - trailingComma: 'es5', - bracketSpacing: true, - tabWidth: 2, - semi: false, - singleQuote: true, - arrowParens: 'always', - overrides: [ - { - files: 'Routes.*', - options: { - printWidth: 999, - }, - }, - ], - tailwindConfig: './web/config/tailwind.config.js', - plugins: ['prettier-plugin-tailwindcss'], -} diff --git a/__fixtures__/test-project/scripts/seed.ts b/__fixtures__/test-project/scripts/seed.ts index eee545d0d8..cdee167538 100644 --- a/__fixtures__/test-project/scripts/seed.ts +++ b/__fixtures__/test-project/scripts/seed.ts @@ -1,4 +1,4 @@ -import { db } from 'api/src/lib/db' +import { db } from 'api/src/lib/db.js' // Manually apply seeds via the `yarn rw prisma db seed` command. // diff --git a/__fixtures__/test-project/web/jest.config.js b/__fixtures__/test-project/web/jest.config.js deleted file mode 100644 index cd4ee4403c..0000000000 --- a/__fixtures__/test-project/web/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', - preset: '@cedarjs/testing/config/jest/web', -} - -module.exports = config diff --git a/__fixtures__/test-project/web/src/App.tsx b/__fixtures__/test-project/web/src/App.tsx index 77fba88c8b..535dc0ae47 100644 --- a/__fixtures__/test-project/web/src/App.tsx +++ b/__fixtures__/test-project/web/src/App.tsx @@ -5,7 +5,7 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import FatalErrorPage from 'src/pages/FatalErrorPage' -import { AuthProvider, useAuth } from './auth' +import { AuthProvider, useAuth } from './auth.js' import './index.css' import './scaffold.css' diff --git a/__fixtures__/test-project/web/src/Routes.tsx b/__fixtures__/test-project/web/src/Routes.tsx index 73fbae0cc4..1894deaa0c 100644 --- a/__fixtures__/test-project/web/src/Routes.tsx +++ b/__fixtures__/test-project/web/src/Routes.tsx @@ -13,7 +13,7 @@ import BlogLayout from 'src/layouts/BlogLayout' import ScaffoldLayout from 'src/layouts/ScaffoldLayout' import HomePage from 'src/pages/HomePage' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( diff --git a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx index 5e8ec77fa4..5fe428b4e7 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contact/Contact.tsx @@ -9,7 +9,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { timeTag } from 'src/lib/formatters' +import { timeTag } from 'src/lib/formatters.js' const DELETE_CONTACT_MUTATION: TypedDocumentNode< DeleteContactMutation, diff --git a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx index e1fd1acf2d..fdc349093f 100644 --- a/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx +++ b/__fixtures__/test-project/web/src/components/Contact/Contacts/Contacts.tsx @@ -10,7 +10,7 @@ import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' import { QUERY } from 'src/components/Contact/ContactsCell' -import { timeTag, truncate } from 'src/lib/formatters' +import { timeTag, truncate } from 'src/lib/formatters.js' const DELETE_CONTACT_MUTATION: TypedDocumentNode< DeleteContactMutation, diff --git a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx index 8b5f5429a8..74a1d83fa9 100644 --- a/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Post/Post.tsx @@ -9,7 +9,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { timeTag } from 'src/lib/formatters' +import { timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, diff --git a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx index 423cf4ae74..2c992a13e2 100644 --- a/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx +++ b/__fixtures__/test-project/web/src/components/Post/Posts/Posts.tsx @@ -10,7 +10,7 @@ import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' -import { timeTag, truncate } from 'src/lib/formatters' +import { timeTag, truncate } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, diff --git a/__fixtures__/test-project/web/src/lib/formatters.test.tsx b/__fixtures__/test-project/web/src/lib/formatters.test.tsx index 75bc89ebea..0383c42716 100644 --- a/__fixtures__/test-project/web/src/lib/formatters.test.tsx +++ b/__fixtures__/test-project/web/src/lib/formatters.test.tsx @@ -7,7 +7,7 @@ import { timeTag, jsonDisplay, checkboxInputTag, -} from './formatters' +} from './formatters.js' describe('formatEnum', () => { it('handles nullish values', () => { diff --git a/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.tsx b/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.tsx index 52aac7a3d6..b685e733b7 100644 --- a/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.tsx +++ b/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.tsx @@ -16,5 +16,4 @@ const BlogPostPage = ({ id }: BlogPostPageProps) => { ) } - export default BlogPostPage diff --git a/__fixtures__/test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx b/__fixtures__/test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx index 6c4f24a14c..50926b0599 100644 --- a/__fixtures__/test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx +++ b/__fixtures__/test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx @@ -7,5 +7,4 @@ type WaterfallPageProps = { const WaterfallPage = ({ id }: WaterfallPageProps) => ( ) - export default WaterfallPage diff --git a/__fixtures__/test-project/web/src/scaffold.css b/__fixtures__/test-project/web/src/scaffold.css index ffa9142b71..736897ab98 100644 --- a/__fixtures__/test-project/web/src/scaffold.css +++ b/__fixtures__/test-project/web/src/scaffold.css @@ -21,26 +21,26 @@ @apply text-gray-500; } .rw-header { - @apply flex justify-between px-8 py-4; + @apply flex justify-between py-4 px-8; } .rw-main { @apply mx-4 pb-4; } .rw-segment { - @apply w-full overflow-hidden rounded-lg border border-gray-200; - scrollbar-color: theme('colors.zinc.400') transparent; + @apply rounded-lg overflow-hidden w-full border border-gray-200; + scrollbar-color: theme("colors.zinc.400") transparent; } .rw-segment::-webkit-scrollbar { height: initial; } .rw-segment::-webkit-scrollbar-track { - @apply rounded-b-[10px] rounded-t-none border-0 border-t border-solid border-gray-200 bg-transparent p-[2px]; + @apply border-gray-200 bg-transparent border-solid rounded-t-none rounded-b-[10px] border-0 border-t p-[2px]; } .rw-segment::-webkit-scrollbar-thumb { - @apply rounded-full border-[3px] border-solid border-transparent bg-zinc-400 bg-clip-content; + @apply bg-zinc-400 bg-clip-content border-[3px] border-solid border-transparent rounded-full; } .rw-segment-header { - @apply bg-gray-200 px-4 py-3 text-gray-700; + @apply bg-gray-200 text-gray-700 py-3 px-4; } .rw-segment-main { @apply bg-gray-100 p-4; @@ -52,7 +52,7 @@ @apply text-blue-500; } .rw-forgot-link { - @apply mt-1 text-right text-xs text-gray-400 underline; + @apply text-xs text-gray-400 text-right mt-1 underline; } .rw-forgot-link:hover { @apply text-blue-500; @@ -76,26 +76,26 @@ @apply text-sm font-semibold; } .rw-form-wrapper { - @apply -mt-4 text-sm; + @apply text-sm -mt-4; } .rw-cell-error, .rw-form-error-wrapper { - @apply my-4 rounded border border-red-100 bg-red-50 p-4 text-red-600; + @apply p-4 bg-red-50 text-red-600 border border-red-100 rounded my-4; } .rw-form-error-title { @apply m-0 font-semibold; } .rw-form-error-list { - @apply mt-2 list-inside list-disc; + @apply mt-2 list-disc list-inside; } .rw-button { - @apply flex cursor-pointer justify-center rounded border-0 bg-gray-200 px-4 py-1 text-xs font-semibold uppercase leading-loose tracking-wide text-gray-500 no-underline transition duration-100; + @apply flex justify-center py-1 px-4 border-0 rounded bg-gray-200 text-gray-500 text-xs font-semibold uppercase tracking-wide leading-loose no-underline cursor-pointer transition duration-100; } .rw-button:hover { @apply bg-gray-500 text-white; } .rw-button.rw-button-small { - @apply rounded-sm px-2 py-1 text-xs; + @apply text-xs rounded-sm py-1 px-2; } .rw-button.rw-button-green { @apply bg-green-500 text-white; @@ -116,10 +116,10 @@ @apply bg-red-700 text-white; } .rw-button-icon { - @apply mr-1 text-xl leading-5; + @apply text-xl leading-5 mr-1; } .rw-button-group { - @apply mx-2 my-3 flex justify-center; + @apply flex justify-center my-3 mx-2; } .rw-button-group .rw-button { @apply mx-1; @@ -128,13 +128,13 @@ @apply mt-8; } .rw-label { - @apply mt-6 block text-left font-semibold text-gray-600; + @apply block mt-6 text-gray-600 font-semibold text-left; } .rw-label.rw-label-error { @apply text-red-600; } .rw-input { - @apply mt-2 block w-full rounded border border-gray-200 bg-white p-2 outline-none; + @apply block mt-2 w-full p-2 bg-white border border-gray-200 rounded outline-none; } .rw-check-radio-items { @apply flex justify-items-center; @@ -142,9 +142,9 @@ .rw-check-radio-item-none { @apply text-gray-600; } -.rw-input[type='checkbox'], -.rw-input[type='radio'] { - @apply ml-0 mr-1 mt-1 inline w-4; +.rw-input[type="checkbox"], +.rw-input[type="radio"] { + @apply inline w-4 ml-0 mr-1 mt-1; } .rw-input:focus { @apply border-gray-400; @@ -157,7 +157,7 @@ box-shadow: 0 0 5px #c53030; } .rw-field-error { - @apply mt-1 block text-xs font-semibold uppercase text-red-600; + @apply block mt-1 font-semibold text-xs text-red-600 uppercase; } .rw-table-wrapper-responsive { @apply overflow-x-auto; @@ -183,7 +183,7 @@ @apply bg-gray-200 text-gray-600; } .rw-table th { - @apply text-left font-semibold; + @apply font-semibold text-left; } .rw-table thead th { @apply text-left; @@ -203,7 +203,7 @@ @apply ml-0; } .rw-table-actions { - @apply flex h-4 items-center justify-end pr-1; + @apply flex justify-end items-center h-4 pr-1; } .rw-table-actions .rw-button { @apply bg-transparent; @@ -227,16 +227,16 @@ @apply text-center; } .rw-login-container { - @apply mx-auto my-16 flex w-96 flex-wrap items-center justify-center; + @apply flex items-center justify-center flex-wrap mx-auto w-96 my-16; } .rw-login-container .rw-form-wrapper { @apply w-full text-center; } .rw-login-link { - @apply mt-4 w-full text-center text-sm text-gray-600; + @apply mt-4 text-gray-600 text-sm text-center w-full; } .rw-webauthn-wrapper { - @apply mx-4 mt-6 leading-6; + @apply mt-6 mx-4 leading-6; } .rw-webauthn-wrapper h2 { @apply mb-4 text-xl font-bold; diff --git a/packages/cli/src/commands/setup/graphql/features/fragments/__codemod_tests__/appGqlConfigTransform.test.ts b/packages/cli/src/commands/setup/graphql/features/fragments/__codemod_tests__/appGqlConfigTransform.test.ts index 15a5cd4742..b878a716ed 100644 --- a/packages/cli/src/commands/setup/graphql/features/fragments/__codemod_tests__/appGqlConfigTransform.test.ts +++ b/packages/cli/src/commands/setup/graphql/features/fragments/__codemod_tests__/appGqlConfigTransform.test.ts @@ -71,7 +71,7 @@ describe('fragments graphQLClientConfig', () => { import FatalErrorPage from \"src/pages/FatalErrorPage\"; - import { AuthProvider, useAuth } from \"./auth\"; + import { AuthProvider, useAuth } from \"./auth.js\"; import \"./index.css\"; import \"./scaffold.css\"; From 38f4cc7bf3d47b608726ae02d08e434380571f22 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 20:43:49 +0200 Subject: [PATCH 010/222] add missing files and fix package.json --- __fixtures__/test-project/api/jest.config.cjs | 8 ++++++++ __fixtures__/test-project/graphql.config.cjs | 11 ++++++++++ __fixtures__/test-project/jest.config.cjs | 8 ++++++++ __fixtures__/test-project/prettier.config.cjs | 20 +++++++++++++++++++ __fixtures__/test-project/web/jest.config.cjs | 8 ++++++++ packages/cli/package.json | 1 - 6 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 __fixtures__/test-project/api/jest.config.cjs create mode 100644 __fixtures__/test-project/graphql.config.cjs create mode 100644 __fixtures__/test-project/jest.config.cjs create mode 100644 __fixtures__/test-project/prettier.config.cjs create mode 100644 __fixtures__/test-project/web/jest.config.cjs diff --git a/__fixtures__/test-project/api/jest.config.cjs b/__fixtures__/test-project/api/jest.config.cjs new file mode 100644 index 0000000000..6646ef5637 --- /dev/null +++ b/__fixtures__/test-project/api/jest.config.cjs @@ -0,0 +1,8 @@ +// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build + +const config = { + rootDir: '../', + preset: '@cedarjs/testing/config/jest/api', +} + +module.exports = config diff --git a/__fixtures__/test-project/graphql.config.cjs b/__fixtures__/test-project/graphql.config.cjs new file mode 100644 index 0000000000..d82bff34e0 --- /dev/null +++ b/__fixtures__/test-project/graphql.config.cjs @@ -0,0 +1,11 @@ +// This file is used by the VSCode GraphQL extension + +const { getPaths } = require('@cedarjs/project-config') + +/** @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/__fixtures__/test-project/jest.config.cjs b/__fixtures__/test-project/jest.config.cjs new file mode 100644 index 0000000000..c6b395cb76 --- /dev/null +++ b/__fixtures__/test-project/jest.config.cjs @@ -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/__fixtures__/test-project/prettier.config.cjs b/__fixtures__/test-project/prettier.config.cjs new file mode 100644 index 0000000000..3ed0f1e84d --- /dev/null +++ b/__fixtures__/test-project/prettier.config.cjs @@ -0,0 +1,20 @@ +// https://prettier.io/docs/en/options.html +/** @type {import('prettier').RequiredOptions} */ +module.exports = { + trailingComma: 'es5', + bracketSpacing: true, + tabWidth: 2, + semi: false, + singleQuote: true, + arrowParens: 'always', + overrides: [ + { + files: 'Routes.*', + options: { + printWidth: 999, + }, + }, + ], + tailwindConfig: './web/config/tailwind.config.js', + plugins: ['prettier-plugin-tailwindcss'], +} diff --git a/__fixtures__/test-project/web/jest.config.cjs b/__fixtures__/test-project/web/jest.config.cjs new file mode 100644 index 0000000000..cd4ee4403c --- /dev/null +++ b/__fixtures__/test-project/web/jest.config.cjs @@ -0,0 +1,8 @@ +// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build + +const config = { + rootDir: '../', + preset: '@cedarjs/testing/config/jest/web', +} + +module.exports = config diff --git a/packages/cli/package.json b/packages/cli/package.json index f8ee7c7903..37b1835a10 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -28,7 +28,6 @@ "test:watch": "vitest watch" }, "dependencies": { - "@babel/core": "^7.26.10", "@babel/preset-typescript": "7.27.1", "@babel/runtime-corejs3": "7.27.1", "@cedarjs/api-server": "workspace:*", From eadfc1b11ea043e990fd9e658aa7e70932578adb Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 21:15:38 +0200 Subject: [PATCH 011/222] also support and aliases --- packages/cli/src/lib/exec.js | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/cli/src/lib/exec.js b/packages/cli/src/lib/exec.js index 6c1a032c2f..69537e576c 100644 --- a/packages/cli/src/lib/exec.js +++ b/packages/cli/src/lib/exec.js @@ -2,6 +2,7 @@ import fs from 'fs' import { createRequire } from 'module' import path from 'path' +// Import babel from devDependencies import * as babel from '@babel/core' import * as esbuild from 'esbuild' @@ -83,16 +84,17 @@ export async function runScriptFunction({ { name: 'redwood-path-resolver', setup(build) { - // Custom resolver for redwood-specific imports - build.onResolve({ filter: /^(api|web)(\/|$)/ }, (args) => { - const match = args.path.match(/^(api|web)(\/|$)/) + // Custom resolver for redwood-specific imports (both with and without $ prefix) + build.onResolve({ filter: /^(\$?api|\$?web)(\/|$)/ }, (args) => { + const match = args.path.match(/^(\$?api|\$?web)(\/|$)/) if (!match) { return null } - const side = match[1] // 'api' or 'web' + // Handle both 'api', '$api', 'web', and '$web' + const side = match[1].replace('$', '') // Remove $ if present, giving us 'api' or 'web' const restOfPath = args.path.slice( - side.length + (match[2] === '/' ? 1 : 0), + match[1].length + (match[2] === '/' ? 1 : 0), ) // Convert .js extension to .ts if needed (or other appropriate extensions) @@ -102,8 +104,6 @@ export async function runScriptFunction({ ) // If the path has a .js extension, check if a corresponding .ts file exists - // If a .ts file exists, it returns that path instead of the .js path - // It also tries `.tsx` as a fallback if (path.extname(fullPath) === '.js') { const tsPath = fullPath.replace(/\.js$/, '.ts') const tsxPath = fullPath.replace(/\.js$/, '.tsx') @@ -213,7 +213,6 @@ export async function runScriptFunction({ return returnValue } catch (error) { - console.error(`Error details: ${error.message}`) if (error.message.includes('Could not resolve')) { throw new Error( `Error bundling '${scriptPath}': ${error.message}. Make sure all imports are available.`, From 680709b3f463cfedd7ecee1608f54c258cf584bf Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 21:32:43 +0200 Subject: [PATCH 012/222] .js in routeHooks --- .../web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts | 2 +- .../web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/__fixtures__/fragment-test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts b/__fixtures__/fragment-test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts index 389ac01837..e29f84e457 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts +++ b/__fixtures__/fragment-test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts @@ -1,4 +1,4 @@ -import { db } from '$api/src/lib/db' +import { db } from '$api/src/lib/db.js' export async function routeParameters() { return (await db.post.findMany({ take: 7 })).map((post) => ({ id: post.id })) diff --git a/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts b/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts index 389ac01837..e29f84e457 100644 --- a/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts +++ b/__fixtures__/test-project/web/src/pages/BlogPostPage/BlogPostPage.routeHooks.ts @@ -1,4 +1,4 @@ -import { db } from '$api/src/lib/db' +import { db } from '$api/src/lib/db.js' export async function routeParameters() { return (await db.post.findMany({ take: 7 })).map((post) => ({ id: post.id })) From ca8f2c1c47ca0421c4cd0c0461a5369ddd8b39b4 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 20 May 2025 21:36:30 +0200 Subject: [PATCH 013/222] ESM file extensions --- packages/cli/src/commands/testHandler.js | 3 ++- tasks/smoke-tests/auth/tests/rbacChecks.spec.ts | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 77c463c176..98a2b76ed4 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -36,12 +36,13 @@ function isJestConfigFile(sides) { if (sides.includes(side)) { const jestConfigExists = fs.existsSync(path.join(side, 'jest.config.js')) || + fs.existsSync(path.join(side, 'jest.config.cjs')) || fs.existsSync(path.join(side, 'jest.config.ts')) if (!jestConfigExists) { console.error( c.error( - `\nError: Missing Jest config file ${side}/jest.config.js` + + `\nError: Missing Jest config file ${side}/jest.config.cjs` + '\nTo add this file, run `npx @cedarjs/codemods update-jest-config`\n', ), ) diff --git a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts index 3b0450d855..fabceace9a 100644 --- a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts +++ b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts @@ -89,7 +89,7 @@ test('RBAC: Admin user should be able to delete contacts', async ({ page }) => { 'scripts/makeAdmin.ts', ), `\ -import { db } from 'api/src/lib/db' +import { db } from 'api/src/lib/db.js' export default async ({ args }) => { await db.user.update({ From 273f61e5b195c427675791a3b7add01b1b5d1d43 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 5 Jun 2025 19:18:53 +0200 Subject: [PATCH 014/222] Dual build `@cedarjs/testing` --- packages/testing/api/index.js | 3 +- packages/testing/build.mts | 18 ++- packages/testing/cache/index.js | 3 +- ...woodApiJestEnv.js => RedwoodApiJestEnv.ts} | 10 +- .../{apiBabelConfig.js => apiBabelConfig.ts} | 15 ++- .../api/{globalSetup.js => globalSetup.ts} | 12 +- packages/testing/config/jest/api/index.js | 2 - packages/testing/config/jest/api/index.ts | 2 + .../api/{jest-preset.js => jest-preset.ts} | 7 +- .../jest/api/{jest.setup.js => jest.setup.ts} | 105 +++++++++++++----- ...woodWebJestEnv.js => RedwoodWebJestEnv.ts} | 6 +- packages/testing/config/jest/web/index.js | 2 - packages/testing/config/jest/web/index.ts | 2 + .../web/{jest-preset.js => jest-preset.ts} | 4 +- .../jest/web/{jest.setup.js => jest.setup.ts} | 12 +- .../jest/web/{resolver.js => resolver.ts} | 4 +- .../testing/config/jest/web/vitest.setup.mts | 91 +++++++++++++++ .../testing/config/jest/web/webBabelConfig.js | 3 - .../testing/config/jest/web/webBabelConfig.ts | 3 + packages/testing/global.d.ts | 46 ++++++++ packages/testing/package.json | 2 +- .../api/__tests__/directUrlHelpers.test.ts | 2 +- packages/testing/src/api/index.ts | 6 +- packages/testing/src/web/MockProviders.tsx | 2 +- .../src/web/__tests__/MockHandlers.test.tsx | 8 +- .../src/web/__tests__/findCellMocks.test.ts | 4 +- packages/testing/src/web/customRender.tsx | 2 +- packages/testing/src/web/global.ts | 11 -- packages/testing/src/web/index.ts | 13 +-- packages/testing/src/web/mockAuth.tsx | 2 +- packages/testing/src/web/mockRequests.ts | 2 +- packages/testing/tsconfig.build.json | 3 +- packages/testing/tsconfig.cjs.json | 7 ++ packages/testing/web/index.js | 3 +- 34 files changed, 312 insertions(+), 105 deletions(-) rename packages/testing/config/jest/api/{RedwoodApiJestEnv.js => RedwoodApiJestEnv.ts} (71%) rename packages/testing/config/jest/api/{apiBabelConfig.js => apiBabelConfig.ts} (68%) rename packages/testing/config/jest/api/{globalSetup.js => globalSetup.ts} (82%) delete mode 100644 packages/testing/config/jest/api/index.js create mode 100644 packages/testing/config/jest/api/index.ts rename packages/testing/config/jest/api/{jest-preset.js => jest-preset.ts} (93%) rename packages/testing/config/jest/api/{jest.setup.js => jest.setup.ts} (78%) rename packages/testing/config/jest/web/{RedwoodWebJestEnv.js => RedwoodWebJestEnv.ts} (68%) delete mode 100644 packages/testing/config/jest/web/index.js create mode 100644 packages/testing/config/jest/web/index.ts rename packages/testing/config/jest/web/{jest-preset.js => jest-preset.ts} (97%) rename packages/testing/config/jest/web/{jest.setup.js => jest.setup.ts} (79%) rename packages/testing/config/jest/web/{resolver.js => resolver.ts} (90%) create mode 100644 packages/testing/config/jest/web/vitest.setup.mts delete mode 100644 packages/testing/config/jest/web/webBabelConfig.js create mode 100644 packages/testing/config/jest/web/webBabelConfig.ts create mode 100644 packages/testing/global.d.ts delete mode 100644 packages/testing/src/web/global.ts create mode 100644 packages/testing/tsconfig.cjs.json diff --git a/packages/testing/api/index.js b/packages/testing/api/index.js index bd05e8c81a..6bebe80d1d 100644 --- a/packages/testing/api/index.js +++ b/packages/testing/api/index.js @@ -1,2 +1 @@ -/* eslint-env es6, commonjs */ -module.exports = require('../dist/api') +export default '../dist/api/index.js' diff --git a/packages/testing/build.mts b/packages/testing/build.mts index a50628ff54..ac1787c17b 100644 --- a/packages/testing/build.mts +++ b/packages/testing/build.mts @@ -1,3 +1,17 @@ -import { build } from '@cedarjs/framework-tools' +import { buildCjs, buildEsm } from '@cedarjs/framework-tools' +import { + generateTypesCjs, + generateTypesEsm, + insertCommonJsPackageJson, +} from '@cedarjs/framework-tools/generateTypes' -await build() +// ESM build and type generation +await buildEsm() +await generateTypesEsm() + +// CJS build, type generation, and package.json insert +await buildCjs() +await generateTypesCjs() +await insertCommonJsPackageJson({ + buildFileUrl: import.meta.url, +}) diff --git a/packages/testing/cache/index.js b/packages/testing/cache/index.js index 215cb6cd0d..a13d8e23e2 100644 --- a/packages/testing/cache/index.js +++ b/packages/testing/cache/index.js @@ -1,2 +1 @@ -/* eslint-env es6, commonjs */ -module.exports = require('../dist/cache') +export default '../dist/cache/index.js' diff --git a/packages/testing/config/jest/api/RedwoodApiJestEnv.js b/packages/testing/config/jest/api/RedwoodApiJestEnv.ts similarity index 71% rename from packages/testing/config/jest/api/RedwoodApiJestEnv.js rename to packages/testing/config/jest/api/RedwoodApiJestEnv.ts index 69cfb33bbc..daa8fa8556 100644 --- a/packages/testing/config/jest/api/RedwoodApiJestEnv.js +++ b/packages/testing/config/jest/api/RedwoodApiJestEnv.ts @@ -1,7 +1,13 @@ -const { TestEnvironment } = require('jest-environment-node') +import type { + EnvironmentContext, + JestEnvironmentConfig, +} from '@jest/environment' +import { TestEnvironment } from 'jest-environment-node' class RedwoodApiJestEnvironment extends TestEnvironment { - constructor(config, context) { + testPath: string + + constructor(config: JestEnvironmentConfig, context: EnvironmentContext) { super(config, context) this.testPath = context.testPath } diff --git a/packages/testing/config/jest/api/apiBabelConfig.js b/packages/testing/config/jest/api/apiBabelConfig.ts similarity index 68% rename from packages/testing/config/jest/api/apiBabelConfig.js rename to packages/testing/config/jest/api/apiBabelConfig.ts index b388ebd28f..ed1aa4dfb7 100644 --- a/packages/testing/config/jest/api/apiBabelConfig.js +++ b/packages/testing/config/jest/api/apiBabelConfig.ts @@ -1,18 +1,25 @@ -const { +import { getApiSideDefaultBabelConfig, getApiSideBabelPresets, getApiSideBabelPlugins, -} = require('@cedarjs/babel-config') +} from '@cedarjs/babel-config' // Since configFile and babelrc is already passed a level up, cleaning up these keys here. // babelrc can not reside inside "extend"ed // Ref: packages/testing/config/jest/api/index.js const { babelrc: _b, ...defaultBabelConfig } = getApiSideDefaultBabelConfig() -module.exports = { +type ConfigType = Omit< + ReturnType, + 'babelrc' +> + +const config: ConfigType = { ...defaultBabelConfig, - plugins: getApiSideBabelPlugins({ forJest: true }), + plugins: getApiSideBabelPlugins(), presets: getApiSideBabelPresets({ presetEnv: true, // jest needs code transpiled }), } + +export default config diff --git a/packages/testing/config/jest/api/globalSetup.js b/packages/testing/config/jest/api/globalSetup.ts similarity index 82% rename from packages/testing/config/jest/api/globalSetup.js rename to packages/testing/config/jest/api/globalSetup.ts index f061fde5a6..e75e00ed11 100644 --- a/packages/testing/config/jest/api/globalSetup.js +++ b/packages/testing/config/jest/api/globalSetup.ts @@ -1,11 +1,11 @@ -const { getSchema } = require('@prisma/internals') +import { getSchema } from '@prisma/internals' -const { getPaths } = require('@cedarjs/project-config') +import { getPaths } from '@cedarjs/project-config' -const { +import { getDefaultDb, checkAndReplaceDirectUrl, -} = require('../../../dist/api/directUrlHelpers') +} from '../../../dist/api/directUrlHelpers.js' const rwjsPaths = getPaths() @@ -39,7 +39,9 @@ module.exports = async function () { shell: true, env: { DATABASE_URL: process.env.DATABASE_URL, - [directUrlEnvVar]: process.env[directUrlEnvVar], + ...(directUrlEnvVar + ? { [directUrlEnvVar]: process.env[directUrlEnvVar] } + : {}), }, }) } diff --git a/packages/testing/config/jest/api/index.js b/packages/testing/config/jest/api/index.js deleted file mode 100644 index 77f8e6c6d4..0000000000 --- a/packages/testing/config/jest/api/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// This is for backwards compatibility -module.exports = require('./jest-preset') diff --git a/packages/testing/config/jest/api/index.ts b/packages/testing/config/jest/api/index.ts new file mode 100644 index 0000000000..2672d2f6ca --- /dev/null +++ b/packages/testing/config/jest/api/index.ts @@ -0,0 +1,2 @@ +// This is for backwards compatibility +export default './jest-preset.js' diff --git a/packages/testing/config/jest/api/jest-preset.js b/packages/testing/config/jest/api/jest-preset.ts similarity index 93% rename from packages/testing/config/jest/api/jest-preset.js rename to packages/testing/config/jest/api/jest-preset.ts index e6f8617afd..cd1e0e9d67 100644 --- a/packages/testing/config/jest/api/jest-preset.js +++ b/packages/testing/config/jest/api/jest-preset.ts @@ -1,8 +1,7 @@ -// @ts-check -const path = require('path') +import path from 'node:path' -const { getApiSideDefaultBabelConfig } = require('@cedarjs/babel-config') -const { getPaths } = require('@cedarjs/project-config') +import { getApiSideDefaultBabelConfig } from '@cedarjs/babel-config' +import { getPaths } from '@cedarjs/project-config' const rwjsPaths = getPaths() const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules') diff --git a/packages/testing/config/jest/api/jest.setup.js b/packages/testing/config/jest/api/jest.setup.ts similarity index 78% rename from packages/testing/config/jest/api/jest.setup.js rename to packages/testing/config/jest/api/jest.setup.ts index 4d9824d82f..a3d4a20f7b 100644 --- a/packages/testing/config/jest/api/jest.setup.js +++ b/packages/testing/config/jest/api/jest.setup.ts @@ -1,32 +1,41 @@ -/* eslint-env jest */ -// @ts-check +// import type { SuiteAPI, TestAPI } from 'vitest' + +import type { Global as jest } from '@jest/types' + +type TestAPI = jest.It +type SuiteAPI = jest.Describe + +declare global { + // eslint-disable-next-line no-var + var mockCurrentUser: (currentUser: Record | null) => void +} // @NOTE without these imports in the setup file, mockCurrentUser // will remain undefined in the user's tests // Remember to use specific imports -const { defineScenario } = require('@cedarjs/testing/dist/api/scenario') +import { defineScenario } from '@cedarjs/testing/dist/api/scenario.js' // @NOTE we do this because jest.setup.js runs every time in each context // while jest-preset runs once. This significantly reduces memory footprint, and testing time // The key is to reduce the amount of imports in this file, because the require.cache is not shared between each test context const { apiSrcPath, tearDownCachePath, dbSchemaPath } = - global.__RWJS__TEST_IMPORTS + globalThis.__RWJS__TEST_IMPORTS -global.defineScenario = defineScenario +globalThis.defineScenario = defineScenario // Error codes thrown by [MySQL, SQLite, Postgres] when foreign key constraint // fails on DELETE const FOREIGN_KEY_ERRORS = [1451, 1811, 23503] const TEARDOWN_CACHE_PATH = tearDownCachePath const DEFAULT_SCENARIO = 'standard' -let teardownOrder = [] -let originalTeardownOrder = [] +let teardownOrder: any[] = [] +let originalTeardownOrder: any[] = [] -const deepCopy = (obj) => { +const deepCopy = (obj: unknown[]) => { return JSON.parse(JSON.stringify(obj)) } -const isIdenticalArray = (a, b) => { +const isIdenticalArray = (a: unknown[], b: unknown[]) => { return JSON.stringify(a) === JSON.stringify(b) } @@ -38,7 +47,9 @@ const configureTeardown = async () => { // But avoid importing them, to prevent memory leaks in jest const datamodel = await getSchema(dbSchemaPath) const schema = await getDMMF({ datamodel }) - const schemaModels = schema.datamodel.models.map((m) => m.dbName || m.name) + const schemaModels = schema.datamodel.models.map( + (m: Record) => m.dbName || m.name, + ) // check if pre-defined delete order already exists and if so, use it to start if (fs.existsSync(TEARDOWN_CACHE_PATH)) { @@ -54,7 +65,7 @@ const configureTeardown = async () => { originalTeardownOrder = deepCopy(teardownOrder) } -let quoteStyle +let quoteStyle: string // determine what kind of quotes are needed around table names in raw SQL const getQuoteStyle = async () => { const { getConfig: getPrismaConfig, getSchema } = require('@prisma/internals') @@ -91,8 +102,16 @@ const getProjectDb = () => { * This one passes scenario data to the test function */ const buildScenario = - (itFunc, testPath) => - (...args) => { + (itFunc: TestAPI, testPath: string) => + ( + ...args: + | [ + scenarioName: string, + testName: string, + testFunc: (scenarioData: any) => any, + ] + | [testName: string, testFunc: (scenarioData: any) => any] + ) => { let scenarioName, testName, testFunc if (args.length === 3) { @@ -105,7 +124,7 @@ const buildScenario = } return itFunc(testName, async () => { - let { scenario } = loadScenarios(testPath, scenarioName) + const { scenario } = loadScenarios(testPath, scenarioName) const scenarioData = await seedScenario(scenario) try { @@ -126,8 +145,12 @@ const buildScenario = * Note that you need to use the getScenario() function to get the data. */ const buildDescribeScenario = - (describeFunc, testPath) => - (...args) => { + (describeFunc: SuiteAPI, testPath: string) => + ( + ...args: + | [string, string, (getScenario: () => any) => any] + | [string, (getScenario: () => any) => any] + ) => { let scenarioName, describeBlockName, describeBlock if (args.length === 3) { @@ -140,9 +163,10 @@ const buildDescribeScenario = } return describeFunc(describeBlockName, () => { - let scenarioData + let scenarioData: Record + beforeAll(async () => { - let { scenario } = loadScenarios(testPath, scenarioName) + const { scenario } = loadScenarios(testPath, scenarioName) scenarioData = await seedScenario(scenario) }) @@ -169,7 +193,8 @@ const teardown = async () => { `DELETE FROM ${quoteStyle}${modelName}${quoteStyle}`, ) } catch (e) { - const match = e.message.match(/Code: `(\d+)`/) + const match = isErrorWithMessage(e) && e.message.match(/Code: `(\d+)`/) + if (match && FOREIGN_KEY_ERRORS.includes(parseInt(match[1]))) { const index = teardownOrder.indexOf(modelName) teardownOrder[index] = null @@ -190,11 +215,13 @@ const teardown = async () => { } } -const seedScenario = async (scenario) => { +const seedScenario = async (scenario: Record) => { if (scenario) { - const scenarios = {} + const scenarios: Record = {} + for (const [model, namedFixtures] of Object.entries(scenario)) { scenarios[model] = {} + for (const [name, createArgs] of Object.entries(namedFixtures)) { if (typeof createArgs === 'function') { scenarios[model][name] = await getProjectDb()[model].create( @@ -206,6 +233,7 @@ const seedScenario = async (scenario) => { } } } + return scenarios } else { return {} @@ -240,7 +268,7 @@ const wasDbUsed = () => { return Object.keys(require.cache).some((module) => { return module === libDbPath }) - } catch (e) { + } catch { // If db wasn't resolved, no point trying to perform db resets return false } @@ -249,7 +277,7 @@ const wasDbUsed = () => { // Attempt to emulate the request context isolation behavior // This is a little more complicated than it would necessarily need to be // but we're following the same pattern as in `@cedarjs/context` -const mockContextStore = new Map() +const mockContextStore = new Map() const mockContext = new Proxy( {}, { @@ -270,7 +298,7 @@ const mockContext = new Proxy( jest.mock('@cedarjs/context', () => { return { context: mockContext, - setContext: (newContext) => { + setContext: (newContext: any) => { mockContextStore.set('context', newContext) }, } @@ -278,7 +306,7 @@ jest.mock('@cedarjs/context', () => { beforeEach(() => { mockContextStore.set('context', {}) }) -global.mockCurrentUser = (currentUser) => { +global.mockCurrentUser = (currentUser: Record | null) => { mockContextStore.set('context', { currentUser }) } @@ -294,7 +322,7 @@ afterAll(async () => { } }) -function loadScenarios(testPath, scenarioName) { +function loadScenarios(testPath: string, scenarioName: string) { const path = require('path') const testFileDir = path.parse(testPath) // e.g. ['comments', 'test'] or ['signup', 'state', 'machine', 'test'] @@ -308,8 +336,14 @@ function loadScenarios(testPath, scenarioName) { allScenarios = require(testFilePath) } catch (e) { // ignore error if scenario file not found, otherwise re-throw - if (e.code !== 'MODULE_NOT_FOUND') { - throw e + if (isErrorWithCode(e)) { + if (e instanceof Error) { + throw e + } else { + console.error('unexpected error type', e) + // eslint-disable-next-line + throw e + } } } @@ -324,3 +358,18 @@ function loadScenarios(testPath, scenarioName) { } return { scenario } } + +function isErrorWithMessage(e: unknown): e is { message: string } { + return ( + !!e && + typeof e === 'object' && + 'message' in e && + typeof e.message === 'string' + ) +} + +function isErrorWithCode(e: unknown): e is { code: string } { + return ( + !!e && typeof e === 'object' && 'code' in e && typeof e.code === 'string' + ) +} diff --git a/packages/testing/config/jest/web/RedwoodWebJestEnv.js b/packages/testing/config/jest/web/RedwoodWebJestEnv.ts similarity index 68% rename from packages/testing/config/jest/web/RedwoodWebJestEnv.js rename to packages/testing/config/jest/web/RedwoodWebJestEnv.ts index 4523d40760..c47d0d83a2 100644 --- a/packages/testing/config/jest/web/RedwoodWebJestEnv.js +++ b/packages/testing/config/jest/web/RedwoodWebJestEnv.ts @@ -1,4 +1,4 @@ -const { TestEnvironment } = require('jest-environment-jsdom') +import { TestEnvironment } from 'jest-environment-jsdom' // Due to issue: https://github.com/jsdom/jsdom/issues/2524 // Fix from: https://github.com/jsdom/jsdom/issues/2524#issuecomment-736672511 @@ -6,11 +6,13 @@ module.exports = class RedwoodWebJestEnv extends TestEnvironment { async setup() { await super.setup() if (typeof this.global.TextEncoder === 'undefined') { - const { TextEncoder, TextDecoder } = require('util') + const { TextEncoder, TextDecoder } = await import('node:util') this.global.TextEncoder = TextEncoder + // @ts-expect-error - TextDecoder from node:utils is close enough this.global.TextDecoder = TextDecoder } if (typeof this.global.crypto.subtle === 'undefined') { + // @ts-expect-error - we're just making sure the object is there this.global.crypto.subtle = {} // To make tests work with auth that use WebCrypto like auth0 } } diff --git a/packages/testing/config/jest/web/index.js b/packages/testing/config/jest/web/index.js deleted file mode 100644 index 77f8e6c6d4..0000000000 --- a/packages/testing/config/jest/web/index.js +++ /dev/null @@ -1,2 +0,0 @@ -// This is for backwards compatibility -module.exports = require('./jest-preset') diff --git a/packages/testing/config/jest/web/index.ts b/packages/testing/config/jest/web/index.ts new file mode 100644 index 0000000000..2672d2f6ca --- /dev/null +++ b/packages/testing/config/jest/web/index.ts @@ -0,0 +1,2 @@ +// This is for backwards compatibility +export default './jest-preset.js' diff --git a/packages/testing/config/jest/web/jest-preset.js b/packages/testing/config/jest/web/jest-preset.ts similarity index 97% rename from packages/testing/config/jest/web/jest-preset.js rename to packages/testing/config/jest/web/jest-preset.ts index affe229602..0feab7560d 100644 --- a/packages/testing/config/jest/web/jest-preset.js +++ b/packages/testing/config/jest/web/jest-preset.ts @@ -1,6 +1,6 @@ -const path = require('path') +import path from 'node:path' -const { getPaths } = require('@cedarjs/project-config') +import { getPaths } from '@cedarjs/project-config' const rwjsPaths = getPaths() const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules') diff --git a/packages/testing/config/jest/web/jest.setup.js b/packages/testing/config/jest/web/jest.setup.ts similarity index 79% rename from packages/testing/config/jest/web/jest.setup.js rename to packages/testing/config/jest/web/jest.setup.ts index 5821e86c7b..988d30ff00 100644 --- a/packages/testing/config/jest/web/jest.setup.js +++ b/packages/testing/config/jest/web/jest.setup.ts @@ -1,17 +1,15 @@ -/* eslint-env jest */ +import '@testing-library/jest-dom' +import 'whatwg-fetch' -require('@testing-library/jest-dom') -require('whatwg-fetch') - -const { findCellMocks } = require('@cedarjs/testing/dist/web/findCellMocks') -const { +import { findCellMocks } from '@cedarjs/testing/dist/web/findCellMocks.js' +import { startMSW, setupRequestHandlers, closeServer, mockGraphQLMutation, mockGraphQLQuery, mockCurrentUser, -} = require('@cedarjs/testing/dist/web/mockRequests') +} from '@cedarjs/testing/dist/web/mockRequests.js' global.mockGraphQLQuery = mockGraphQLQuery global.mockGraphQLMutation = mockGraphQLMutation diff --git a/packages/testing/config/jest/web/resolver.js b/packages/testing/config/jest/web/resolver.ts similarity index 90% rename from packages/testing/config/jest/web/resolver.js rename to packages/testing/config/jest/web/resolver.ts index 0321a63626..f1670be83c 100644 --- a/packages/testing/config/jest/web/resolver.js +++ b/packages/testing/config/jest/web/resolver.ts @@ -5,10 +5,10 @@ // TL;DR, we need to resolve uuid to a CommonJS version. So we leverage jest's default resolver, // but use `packageFilter` to process parsed `package.json` before resolution. In doing so, // we only override how jest resolves uuid. -module.exports = (path, options) => { +module.exports = (path: string, options: any) => { return options.defaultResolver(path, { ...options, - packageFilter: (pkg) => { + packageFilter: (pkg: Record) => { if (OVERRIDE_EXPORTS_LIST.has(pkg.name)) { delete pkg['exports'] delete pkg['module'] diff --git a/packages/testing/config/jest/web/vitest.setup.mts b/packages/testing/config/jest/web/vitest.setup.mts new file mode 100644 index 0000000000..0d0230d3d9 --- /dev/null +++ b/packages/testing/config/jest/web/vitest.setup.mts @@ -0,0 +1,91 @@ +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { getPaths } from '@cedarjs/project-config' + +const rwjsPaths = getPaths() +const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules') + +export default defineConfig({ + test: { + root: rwjsPaths.base, + + // TODO: Allow the user to override this if they want to switch to + // 'happy-dom' for better performance + environment: 'jsdom', + + include: [path.join(rwjsPaths.web.src, '**/*.{test,spec}.{js,jsx,ts,tsx}')], + + // TODO: Set this to 'false', and let the user configure this on their own + // if this is something they want + // Enables global test APIs like describe, it, expect + globals: true, + + setupFiles: [path.resolve(__dirname, './vitest.setup.js')], + + coverage: { + provider: 'v8', // or 'c8' or 'istanbul' + include: ['**/*.{js,jsx,ts,tsx}'], + exclude: ['**/node_modules/**', '**/dist/**'], + reportsDirectory: path.join(rwjsPaths.base, 'coverage'), + }, + + exclude: [ + '**/node_modules/**', + '**/dist/**', + '**/*.stories.{js,jsx,ts,tsx}', + '**/*.mock.{js,jsx,ts,tsx}', + ], + }, + + // Module resolution + resolve: { + alias: { + // Make sure modules use the same versions + react: path.join(NODE_MODULES_PATH, 'react'), + 'react-dom': path.join(NODE_MODULES_PATH, 'react-dom'), + '@apollo/client/react': path.join( + NODE_MODULES_PATH, + '@apollo/client/react', + ), + + // Mock implementations + '@cedarjs/router': path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/MockRouter.js', + ), + '@cedarjs/web': path.join(NODE_MODULES_PATH, '@cedarjs/web/dist/cjs'), + '@cedarjs/auth': path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/mockAuth.js', + ), + '@cedarjs/testing': path.join(NODE_MODULES_PATH, '@cedarjs/testing/web'), + + // Special Redwood paths + '~__REDWOOD__USER_ROUTES_FOR_MOCK': rwjsPaths.web.routes, + '~__REDWOOD__USER_AUTH_FOR_MOCK': path.join(rwjsPaths.web.src, 'auth'), + + // File mocks for assets + '\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|css)$': + '@cedarjs/testing/dist/web/fileMock.js', + }, + }, + + // Define global variables for tests + define: { + __RWJS_TESTROOT_DIR: JSON.stringify(path.join(rwjsPaths.web.src)), + 'process.env.RWJS_API_URL': JSON.stringify(''), + 'process.env.RWJS_API_GRAPHQL_URL': JSON.stringify('/'), + 'process.env.REDWOOD_APP_TITLE': JSON.stringify('Redwood App'), + 'process.env.RWJS_SRC_ROOT': JSON.stringify(rwjsPaths.web.src), + RWJS_ENV: { + RWJS_API_URL: '', + RWJS_API_GRAPHQL_URL: '/', + __REDWOOD__APP_TITLE: 'Redwood App', + }, + RWJS_DEBUG_ENV: { + RWJS_SRC_ROOT: rwjsPaths.web.src, + }, + }, +}) diff --git a/packages/testing/config/jest/web/webBabelConfig.js b/packages/testing/config/jest/web/webBabelConfig.js deleted file mode 100644 index f30d4a94d7..0000000000 --- a/packages/testing/config/jest/web/webBabelConfig.js +++ /dev/null @@ -1,3 +0,0 @@ -const { getWebSideDefaultBabelConfig } = require('@cedarjs/babel-config') - -module.exports = getWebSideDefaultBabelConfig({ forJest: true }) diff --git a/packages/testing/config/jest/web/webBabelConfig.ts b/packages/testing/config/jest/web/webBabelConfig.ts new file mode 100644 index 0000000000..0de50adb13 --- /dev/null +++ b/packages/testing/config/jest/web/webBabelConfig.ts @@ -0,0 +1,3 @@ +import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' + +module.exports = getWebSideDefaultBabelConfig({ forJest: true }) diff --git a/packages/testing/global.d.ts b/packages/testing/global.d.ts new file mode 100644 index 0000000000..1c07ea8a44 --- /dev/null +++ b/packages/testing/global.d.ts @@ -0,0 +1,46 @@ +/* eslint-disable no-var */ +import type { Global as jest } from '@jest/types' +// import type { SuiteAPI, TestAPI } from 'vitest' +// import type { SuiteAPI } from 'vitest' +// type TestAPI = (name: string, test: () => void | Promise) => void +type TestAPI = jest.It +type SuiteAPI = jest.Describe + +import type { + mockGraphQLMutation as mockGqlMutation, + mockGraphQLQuery as mockGqlQuery, +} from '@cedarjs/testing/src/web/mockRequests.js' + +import type { DefineScenario } from './src/api/scenario.ts' + +declare global { + var scenario: ( + ...args: + | [ + scenarioName: string, + testName: string, + testFunc: (scenarioData: any) => any, + ] + | [testName: string, testFunc: (scenarioData: any) => any] + ) => void + var describeScenario: ( + ...args: + | [string, string, (getScenario: () => any) => any] + | [string, (getScenario: () => any) => any] + ) => ReturnType + var describe: SuiteAPI + var it: TestAPI + var testPath: string + var defineScenario: DefineScenario + + // var mockCurrentUser: (currentUser: Record | null) => void + var mockGraphQLMutation: typeof mockGqlMutation + var mockGraphQLQuery: typeof mockGqlQuery + + var __RWJS__TEST_IMPORTS: { + apiSrcPath: string + tearDownCachePath: string + dbSchemaPath: string + } + var __RWJS_TESTROOT_DIR: string +} diff --git a/packages/testing/package.json b/packages/testing/package.json index c3cc432b63..be61e41281 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -8,7 +8,7 @@ "directory": "packages/testing" }, "license": "MIT", - "type": "commonjs", + "type": "module", "files": [ "config", "web", diff --git a/packages/testing/src/api/__tests__/directUrlHelpers.test.ts b/packages/testing/src/api/__tests__/directUrlHelpers.test.ts index 3cb207764e..6dd2583aed 100644 --- a/packages/testing/src/api/__tests__/directUrlHelpers.test.ts +++ b/packages/testing/src/api/__tests__/directUrlHelpers.test.ts @@ -3,7 +3,7 @@ import path from 'path' import { it, expect } from 'vitest' -import { checkAndReplaceDirectUrl, getDefaultDb } from '../directUrlHelpers' +import { checkAndReplaceDirectUrl, getDefaultDb } from '../directUrlHelpers.js' const FIXTURE_DIR_PATH = path.resolve('..', '..', '__fixtures__') diff --git a/packages/testing/src/api/index.ts b/packages/testing/src/api/index.ts index d4349bd293..da494f0ac2 100644 --- a/packages/testing/src/api/index.ts +++ b/packages/testing/src/api/index.ts @@ -1,3 +1,3 @@ -export * from './apiFunction' -export * from './scenario' -export * from './directive' +export * from './apiFunction.js' +export * from './scenario.js' +export * from './directive.js' diff --git a/packages/testing/src/web/MockProviders.tsx b/packages/testing/src/web/MockProviders.tsx index 8346307151..f0b599e382 100644 --- a/packages/testing/src/web/MockProviders.tsx +++ b/packages/testing/src/web/MockProviders.tsx @@ -10,7 +10,7 @@ import { LocationProvider } from '@cedarjs/router' import { RedwoodProvider } from '@cedarjs/web' import { RedwoodApolloProvider } from '@cedarjs/web/apollo' -import { MockParamsProvider } from './MockParamsProvider' +import { MockParamsProvider } from './MockParamsProvider.js' // Import the user's Routes from `./web/src/Routes.{tsx,jsx}`, // we pass the `children` from the user's Routes to `./MockRouter.Router` diff --git a/packages/testing/src/web/__tests__/MockHandlers.test.tsx b/packages/testing/src/web/__tests__/MockHandlers.test.tsx index 1d1ad73c60..e0d7ccc86a 100644 --- a/packages/testing/src/web/__tests__/MockHandlers.test.tsx +++ b/packages/testing/src/web/__tests__/MockHandlers.test.tsx @@ -4,7 +4,7 @@ import { render, screen, fireEvent, waitFor } from '@testing-library/react' import { vi, describe, it, expect } from 'vitest' import 'whatwg-fetch' -import { mockGraphQLQuery } from '../mockRequests' +import { mockGraphQLQuery } from '../mockRequests.js' vi.setConfig({ testTimeout: 6_000 }) @@ -86,7 +86,7 @@ describe('GraphQLMockHandlers', () => { await waitFor( () => - expect(JSON.parse(result.textContent)).toEqual({ + expect(JSON.parse(result.textContent || '')).toEqual({ data: onceResult, }), { @@ -98,7 +98,7 @@ describe('GraphQLMockHandlers', () => { await waitFor( () => - expect(JSON.parse(result.textContent)).toEqual({ + expect(JSON.parse(result.textContent || '')).toEqual({ data: baseResult, }), { @@ -111,7 +111,7 @@ describe('GraphQLMockHandlers', () => { await waitFor( () => { expect(status).toHaveTextContent('false') - expect(JSON.parse(result.textContent)).toEqual({ + expect(JSON.parse(result.textContent || '')).toEqual({ data: baseResult, }) }, diff --git a/packages/testing/src/web/__tests__/findCellMocks.test.ts b/packages/testing/src/web/__tests__/findCellMocks.test.ts index 397a5cbe6d..7915469f4a 100644 --- a/packages/testing/src/web/__tests__/findCellMocks.test.ts +++ b/packages/testing/src/web/__tests__/findCellMocks.test.ts @@ -4,14 +4,14 @@ import { test, expect } from 'vitest' import { ensurePosixPath } from '@cedarjs/project-config' -import { findCellMocks } from '../findCellMocks' +import { findCellMocks } from '../findCellMocks.js' const FIXTURE_PATH = path.resolve( __dirname, '../../../../../__fixtures__/example-todo-main', ) -const cleanPaths = (p) => { +const cleanPaths = (p: string) => { return ensurePosixPath(path.relative(FIXTURE_PATH, p)) } diff --git a/packages/testing/src/web/customRender.tsx b/packages/testing/src/web/customRender.tsx index de0133eee9..53aca69f6e 100644 --- a/packages/testing/src/web/customRender.tsx +++ b/packages/testing/src/web/customRender.tsx @@ -8,7 +8,7 @@ import type { RenderHookResult, } from '@testing-library/react' -import { MockProviders } from './MockProviders' +import { MockProviders } from './MockProviders.js' export type CustomRenderHookOptions = RenderHookOptions diff --git a/packages/testing/src/web/global.ts b/packages/testing/src/web/global.ts deleted file mode 100644 index 363a19450a..0000000000 --- a/packages/testing/src/web/global.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { - mockGraphQLQuery as _mockGraphQLQuery, - mockGraphQLMutation as _mockGraphQLMutation, -} from './mockRequests' - -declare global { - const mockGraphQLQuery: typeof _mockGraphQLQuery - const mockGraphQLMutation: typeof _mockGraphQLMutation - // @NOTE: not exposing mockCurrentUser here, because api side also has this functionality - // We do this in the type generator -} diff --git a/packages/testing/src/web/index.ts b/packages/testing/src/web/index.ts index e35a6e283e..9972123c82 100644 --- a/packages/testing/src/web/index.ts +++ b/packages/testing/src/web/index.ts @@ -1,16 +1,15 @@ -// https://testing-library.com/docs/react-testing-library/setup#custom-render -import './global' - export * from '@testing-library/react' + +// https://testing-library.com/docs/react-testing-library/setup#custom-render export { customRender as render, customRenderHook as renderHook, -} from './customRender' +} from './customRender.js' -export { MockProviders } from './MockProviders' +export { MockProviders } from './MockProviders.js' -export { useAuth } from './mockAuth' +export { useAuth } from './mockAuth.js' -export * from './mockRequests' +export * from './mockRequests.js' // @NOTE Intentionally not exporting findCellMocks here diff --git a/packages/testing/src/web/mockAuth.tsx b/packages/testing/src/web/mockAuth.tsx index 99e4f2cc1a..9fc95cefe3 100644 --- a/packages/testing/src/web/mockAuth.tsx +++ b/packages/testing/src/web/mockAuth.tsx @@ -4,7 +4,7 @@ import React from 'react' // override exports with the same name export * from '@cedarjs/auth' -import { mockedUserMeta } from './mockRequests' +import { mockedUserMeta } from './mockRequests.js' interface Props { children: React.ReactNode diff --git a/packages/testing/src/web/mockRequests.ts b/packages/testing/src/web/mockRequests.ts index a4771465e2..0d3f76e6c5 100644 --- a/packages/testing/src/web/mockRequests.ts +++ b/packages/testing/src/web/mockRequests.ts @@ -41,7 +41,7 @@ export const startMSW = async ( SERVER_INSTANCE = setupWorker() await SERVER_INSTANCE.start(options) } else { - const { setupServer } = require('msw/node') + const { setupServer } = await import('msw/node') SERVER_INSTANCE = setupServer() await SERVER_INSTANCE.listen(options) } diff --git a/packages/testing/tsconfig.build.json b/packages/testing/tsconfig.build.json index 4fbf566913..284e6cf46e 100644 --- a/packages/testing/tsconfig.build.json +++ b/packages/testing/tsconfig.build.json @@ -7,7 +7,8 @@ "moduleResolution": "Node16", "tsBuildInfoFile": "./tsconfig.build.tsbuildinfo" }, - "include": ["src"], + "include": ["."], + "exclude": ["dist", "node_modules", "**/__mocks__", "**/__fixtures__"], "references": [ { "path": "../router/tsconfig.build.json" }, { "path": "../babel-config" }, diff --git a/packages/testing/tsconfig.cjs.json b/packages/testing/tsconfig.cjs.json new file mode 100644 index 0000000000..a660cecf11 --- /dev/null +++ b/packages/testing/tsconfig.cjs.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.build.json", + "compilerOptions": { + "outDir": "dist/cjs", + "tsBuildInfoFile": "./tsconfig.cjs.tsbuildinfo" + } +} diff --git a/packages/testing/web/index.js b/packages/testing/web/index.js index fb6512f8a7..01107a482d 100644 --- a/packages/testing/web/index.js +++ b/packages/testing/web/index.js @@ -1,2 +1 @@ -/* eslint-env es6, commonjs */ -module.exports = require('../dist/web') +export default '../dist/web/index.js' From b0be9032fcb7b32b87e60317dcc8ec5db6b3ffc0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 5 Jun 2025 21:48:58 +0200 Subject: [PATCH 015/222] Make react-helmet-async work with both cjs and esm --- packages/web/src/components/MetaTags.tsx | 2 +- packages/web/src/components/Metadata.tsx | 2 +- packages/web/src/components/RedwoodProvider.tsx | 2 +- packages/web/src/index.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/web/src/components/MetaTags.tsx b/packages/web/src/components/MetaTags.tsx index 57132e69b6..56ba7bdb37 100644 --- a/packages/web/src/components/MetaTags.tsx +++ b/packages/web/src/components/MetaTags.tsx @@ -2,7 +2,7 @@ import React from 'react' import * as helmetPkg from 'react-helmet-async' -const { Helmet: HelmetHead } = helmetPkg +const { Helmet: HelmetHead } = helmetPkg.default || helmetPkg // Ideally we wouldn't include this for non experiment builds // But.... not worth the effort to remove it from bundle atm import PortalHead from './PortalHead.js' diff --git a/packages/web/src/components/Metadata.tsx b/packages/web/src/components/Metadata.tsx index 5ca4862ad5..280cf4d1a3 100644 --- a/packages/web/src/components/Metadata.tsx +++ b/packages/web/src/components/Metadata.tsx @@ -4,7 +4,7 @@ import React from 'react' import * as helmetPkg from 'react-helmet-async' -const { Helmet: HelmetHead } = helmetPkg +const { Helmet: HelmetHead } = helmetPkg.default || helmetPkg // Ideally we wouldn't include this for non experiment builds // But.... not worth the effort to remove it from bundle atm diff --git a/packages/web/src/components/RedwoodProvider.tsx b/packages/web/src/components/RedwoodProvider.tsx index 4d215a7fe1..89f527c93e 100644 --- a/packages/web/src/components/RedwoodProvider.tsx +++ b/packages/web/src/components/RedwoodProvider.tsx @@ -2,7 +2,7 @@ import React from 'react' // @NOTE: Helmet is not used in SSR & RSC import * as helmetPkg from 'react-helmet-async' -const { Helmet, HelmetProvider } = helmetPkg +const { Helmet, HelmetProvider } = helmetPkg.default || helmetPkg interface RedwoodProviderProps { children: React.ReactNode diff --git a/packages/web/src/index.ts b/packages/web/src/index.ts index 3a93867d29..4c96044a45 100644 --- a/packages/web/src/index.ts +++ b/packages/web/src/index.ts @@ -34,7 +34,7 @@ export * from './components/MetaTags.js' export * from './components/Metadata.js' import * as helmetPkg from 'react-helmet-async' -const { Helmet } = helmetPkg +const { Helmet } = helmetPkg.default || helmetPkg export { Helmet as Head, Helmet } export * from './components/htmlTags.js' From a816d8576f32ffa0787d2922e98c1488df9cdb9b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 5 Jun 2025 22:31:39 +0200 Subject: [PATCH 016/222] update web/vite.config in create-cedar-app templates --- .../templates/js/web/vite.config.js | 10 ++++++---- .../templates/ts/web/vite.config.ts | 13 ++++++++----- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/packages/create-cedar-app/templates/js/web/vite.config.js b/packages/create-cedar-app/templates/js/web/vite.config.js index a452f9e609..8513ce9298 100644 --- a/packages/create-cedar-app/templates/js/web/vite.config.js +++ b/packages/create-cedar-app/templates/js/web/vite.config.js @@ -8,8 +8,10 @@ import redwood from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -const viteConfig = { +export default defineConfig({ plugins: [redwood()], -} - -export default defineConfig(viteConfig) + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/packages/create-cedar-app/templates/ts/web/vite.config.ts b/packages/create-cedar-app/templates/ts/web/vite.config.ts index 02d441aa13..a326524797 100644 --- a/packages/create-cedar-app/templates/ts/web/vite.config.ts +++ b/packages/create-cedar-app/templates/ts/web/vite.config.ts @@ -1,6 +1,7 @@ +/// + import dns from 'dns' -import type { UserConfig } from 'vite' import { defineConfig } from 'vite' import redwood from '@cedarjs/vite' @@ -9,8 +10,10 @@ import redwood from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -const viteConfig: UserConfig = { +export default defineConfig({ plugins: [redwood()], -} - -export default defineConfig(viteConfig) + test: { + environment: 'jsdom', + globals: true, + }, +}) From ee281e10b329e014fcebe04b60466f048ec1b63c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 5 Jun 2025 22:32:07 +0200 Subject: [PATCH 017/222] update cedarjs/testing build config --- packages/testing/tsconfig.build.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/testing/tsconfig.build.json b/packages/testing/tsconfig.build.json index 284e6cf46e..549abfd64e 100644 --- a/packages/testing/tsconfig.build.json +++ b/packages/testing/tsconfig.build.json @@ -7,8 +7,8 @@ "moduleResolution": "Node16", "tsBuildInfoFile": "./tsconfig.build.tsbuildinfo" }, - "include": ["."], - "exclude": ["dist", "node_modules", "**/__mocks__", "**/__fixtures__"], + "include": ["src"], + "exclude": ["**/__tests__"], "references": [ { "path": "../router/tsconfig.build.json" }, { "path": "../babel-config" }, From 9b658c43f813dc740951ac212374bb62588d005c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 7 Jun 2025 00:30:00 +0200 Subject: [PATCH 018/222] Move vitest to regular dependency --- packages/testing/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/testing/package.json b/packages/testing/package.json index be61e41281..21e8e32783 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -49,6 +49,7 @@ "jest-watch-typeahead": "2.2.2", "msw": "1.3.4", "ts-toolbelt": "9.6.0", + "vitest": "2.1.9", "whatwg-fetch": "3.6.20" }, "devDependencies": { @@ -57,8 +58,7 @@ "jsdom": "24.1.3", "publint": "0.3.11", "tsx": "4.19.3", - "typescript": "5.6.2", - "vitest": "2.1.9" + "typescript": "5.6.2" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } From 997c1dd5b88df9ced5ef03c9e68e995eebbd9b5c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 7 Jun 2025 00:57:56 +0200 Subject: [PATCH 019/222] Update test project --- __fixtures__/test-project/web/vite.config.ts | 13 ++++++++----- .../templates/js/web/vite.config.js | 2 ++ packages/testing/package.json | 1 + 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/__fixtures__/test-project/web/vite.config.ts b/__fixtures__/test-project/web/vite.config.ts index 02d441aa13..a326524797 100644 --- a/__fixtures__/test-project/web/vite.config.ts +++ b/__fixtures__/test-project/web/vite.config.ts @@ -1,6 +1,7 @@ +/// + import dns from 'dns' -import type { UserConfig } from 'vite' import { defineConfig } from 'vite' import redwood from '@cedarjs/vite' @@ -9,8 +10,10 @@ import redwood from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -const viteConfig: UserConfig = { +export default defineConfig({ plugins: [redwood()], -} - -export default defineConfig(viteConfig) + test: { + environment: 'jsdom', + globals: true, + }, +}) diff --git a/packages/create-cedar-app/templates/js/web/vite.config.js b/packages/create-cedar-app/templates/js/web/vite.config.js index 8513ce9298..a326524797 100644 --- a/packages/create-cedar-app/templates/js/web/vite.config.js +++ b/packages/create-cedar-app/templates/js/web/vite.config.js @@ -1,3 +1,5 @@ +/// + import dns from 'dns' import { defineConfig } from 'vite' diff --git a/packages/testing/package.json b/packages/testing/package.json index 21e8e32783..6f615631ea 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -20,6 +20,7 @@ "build": "tsx ./build.mts && yarn build:types", "build:pack": "yarn pack -o cedarjs-testing.tgz", "build:types": "tsc --build --verbose ./tsconfig.build.json", + "build:types-cjs": "tsc --build --verbose tsconfig.cjs.json", "build:watch": "nodemon --watch src --ext 'js,jsx,ts,tsx' --ignore dist --exec 'yarn build'", "check:attw": "yarn rw-fwtools-attw", "check:package": "concurrently npm:check:attw yarn:publint", From 2c71b6680e4a84290a067dd58a4e8ebd8e0489c3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 27 Jun 2025 20:30:38 +0200 Subject: [PATCH 020/222] update test project fixture --- __fixtures__/test-project/web/jest.config.cjs | 1 - packages/create-cedar-app/templates/js/web/jest.config.cjs | 1 - packages/create-cedar-app/templates/ts/web/jest.config.cjs | 1 - tasks/test-project/tasks.js | 2 +- 4 files changed, 1 insertion(+), 4 deletions(-) diff --git a/__fixtures__/test-project/web/jest.config.cjs b/__fixtures__/test-project/web/jest.config.cjs index cd4ee4403c..d81d7f1d14 100644 --- a/__fixtures__/test-project/web/jest.config.cjs +++ b/__fixtures__/test-project/web/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/web', } module.exports = config diff --git a/packages/create-cedar-app/templates/js/web/jest.config.cjs b/packages/create-cedar-app/templates/js/web/jest.config.cjs index cd4ee4403c..d81d7f1d14 100644 --- a/packages/create-cedar-app/templates/js/web/jest.config.cjs +++ b/packages/create-cedar-app/templates/js/web/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/web', } module.exports = config diff --git a/packages/create-cedar-app/templates/ts/web/jest.config.cjs b/packages/create-cedar-app/templates/ts/web/jest.config.cjs index cd4ee4403c..d81d7f1d14 100644 --- a/packages/create-cedar-app/templates/ts/web/jest.config.cjs +++ b/packages/create-cedar-app/templates/ts/web/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/web', } module.exports = config diff --git a/tasks/test-project/tasks.js b/tasks/test-project/tasks.js index e8408af6ac..98f77fbfd4 100644 --- a/tasks/test-project/tasks.js +++ b/tasks/test-project/tasks.js @@ -558,7 +558,7 @@ export default DoublePage` ) fs.writeFileSync(pathRoutes, resultsRoutesNewContact) - const blogPostRouteHooks = `import { db } from '$api/src/lib/db' + const blogPostRouteHooks = `import { db } from '$api/src/lib/db.js' export async function routeParameters() { return (await db.post.findMany({ take: 7 })).map((post) => ({ id: post.id })) From e22b7c2952a23b7309faeec6a5ffdbdf86b09357 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 27 Jun 2025 22:14:07 +0200 Subject: [PATCH 021/222] fix extension --- tasks/test-project/tui-tasks.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tasks/test-project/tui-tasks.ts b/tasks/test-project/tui-tasks.ts index 227b294fae..8171c02f71 100644 --- a/tasks/test-project/tui-tasks.ts +++ b/tasks/test-project/tui-tasks.ts @@ -75,10 +75,7 @@ function createBuilder(cmd: string, dir = '') { } } -export async function webTasks( - outputPath: string, - { linkWithLatestFwBuild }: { linkWithLatestFwBuild: boolean }, -) { +export async function webTasks(outputPath: string) { OUTPUT_PATH = outputPath const execaOptions = getExecaOptions(outputPath) @@ -671,7 +668,7 @@ export default DoublePage` ) fs.writeFileSync(pathRoutes, resultsRoutesNewContact) - const blogPostRouteHooks = `import { db } from '$api/src/lib/db' + const blogPostRouteHooks = `import { db } from '$api/src/lib/db.js' export async function routeParameters() { return (await db.post.findMany({ take: 7 })).map((post) => ({ id: post.id })) From f5b173f2a2e17a58c4b396e8d970440a9e127fd9 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 27 Jun 2025 22:31:53 +0200 Subject: [PATCH 022/222] Remove jest preset --- packages/create-cedar-app/templates/js/api/jest.config.cjs | 1 - packages/create-cedar-app/templates/ts/api/jest.config.cjs | 1 - 2 files changed, 2 deletions(-) diff --git a/packages/create-cedar-app/templates/js/api/jest.config.cjs b/packages/create-cedar-app/templates/js/api/jest.config.cjs index 6646ef5637..d81d7f1d14 100644 --- a/packages/create-cedar-app/templates/js/api/jest.config.cjs +++ b/packages/create-cedar-app/templates/js/api/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/api', } module.exports = config diff --git a/packages/create-cedar-app/templates/ts/api/jest.config.cjs b/packages/create-cedar-app/templates/ts/api/jest.config.cjs index 6646ef5637..d81d7f1d14 100644 --- a/packages/create-cedar-app/templates/ts/api/jest.config.cjs +++ b/packages/create-cedar-app/templates/ts/api/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/api', } module.exports = config From 747ad88d881cd4cdbed00e20be75376a6abb832b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 29 Jun 2025 23:05:02 +0200 Subject: [PATCH 023/222] ESM compatible `@apollo/client` imports --- packages/web/src/apollo/index.tsx | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/packages/web/src/apollo/index.tsx b/packages/web/src/apollo/index.tsx index c67e8d1fa1..8d6cce39e7 100644 --- a/packages/web/src/apollo/index.tsx +++ b/packages/web/src/apollo/index.tsx @@ -1,5 +1,7 @@ import React from 'react' +// @apollo/client ESM support is discussed here +// https://github.com/apollographql/apollo-feature-requests/issues/287 import type { ApolloClientOptions, setLogVerbosity, @@ -7,17 +9,15 @@ import type { InMemoryCacheConfig, HttpOptions, DocumentNode, + HttpLink, } from '@apollo/client' +import { InMemoryCache } from '@apollo/client/cache/cache.cjs' import { - ApolloProvider, ApolloClient, - InMemoryCache, - split, - ApolloLink, -} from '@apollo/client' -import { setLogVerbosity as apolloSetLogVerbosity } from '@apollo/client/core/core.cjs' + setLogVerbosity as apolloSetLogVerbosity, +} from '@apollo/client/core/core.cjs' import { setContext } from '@apollo/client/link/context/context.cjs' -import type { HttpLink } from '@apollo/client/link/http/http.cjs' +import { ApolloLink, split } from '@apollo/client/link/core/core.cjs' import { createPersistedQueryLink } from '@apollo/client/link/persisted-queries/persisted-queries.cjs' import { useQuery, @@ -27,6 +27,7 @@ import { useReadQuery, useSuspenseQuery, } from '@apollo/client/react/hooks/hooks.cjs' +import { ApolloProvider } from '@apollo/client/react/react.cjs' import { getMainDefinition } from '@apollo/client/utilities/utilities.cjs' import { print } from 'graphql/language/printer.js' From 668de797b235b907de6e27b0ddfd3827a04c120c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 3 Jul 2025 11:46:38 +0200 Subject: [PATCH 024/222] Add vitest bin --- packages/core/package.json | 3 ++- packages/core/src/bins/vitest.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 packages/core/src/bins/vitest.ts diff --git a/packages/core/package.json b/packages/core/package.json index d5a25db2be..091138e9be 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -26,7 +26,8 @@ "rw-serve-api": "./dist/bins/rw-serve-api.js", "rw-serve-fe": "./dist/bins/rw-serve-fe.js", "rw-web-server": "./dist/bins/rw-web-server.js", - "rwfw": "./dist/bins/rwfw.js" + "rwfw": "./dist/bins/rwfw.js", + "vitest": "./dist/bins/vitest.js" }, "files": [ "dist" diff --git a/packages/core/src/bins/vitest.ts b/packages/core/src/bins/vitest.ts new file mode 100644 index 0000000000..98fd3dc2e0 --- /dev/null +++ b/packages/core/src/bins/vitest.ts @@ -0,0 +1,9 @@ +#!/usr/bin/env node +import { createRequire } from 'node:module' + +const require = createRequire(import.meta.url) +const requireFromVitest = createRequire(require.resolve('vitest/package.json')) + +const bin = requireFromVitest('./package.json')['bin'] + +requireFromVitest(bin) From 72efef50576b8fe151222c994ae50fae23b4ac0f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 00:04:03 +0200 Subject: [PATCH 025/222] fix vitest bin --- packages/core/src/bins/vitest.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/bins/vitest.ts b/packages/core/src/bins/vitest.ts index 98fd3dc2e0..8c3f8942a3 100644 --- a/packages/core/src/bins/vitest.ts +++ b/packages/core/src/bins/vitest.ts @@ -6,4 +6,4 @@ const requireFromVitest = createRequire(require.resolve('vitest/package.json')) const bin = requireFromVitest('./package.json')['bin'] -requireFromVitest(bin) +requireFromVitest(bin['vitest']) From 64a602959f6be46984248c5c8878dc3b9ab85770 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 13:26:17 +0200 Subject: [PATCH 026/222] vitest plugins and config --- .../mockProvidersRelativeRoutesPathsPlugin.ts | 17 ++++ .../src/vitest/mockProvidersRoutesPlugin.ts | 28 +++++++ packages/testing/src/vitest/vitest-config.ts | 83 +++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts create mode 100644 packages/testing/src/vitest/mockProvidersRoutesPlugin.ts create mode 100644 packages/testing/src/vitest/vitest-config.ts diff --git a/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts b/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts new file mode 100644 index 0000000000..4fbc7aaabd --- /dev/null +++ b/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts @@ -0,0 +1,17 @@ +import type { Plugin } from 'vite' + +export function mockProvidersRelativeRoutesPathsPlugin() { + return { + name: 'cedarjs:mock-providers-relative-routes-paths', + resolveId(id, importer) { + // This is needed to resolve relative paths in the Routes.tsx file when + // it's imported by the virtual 'cedarjs:/Routes.tsx' module in + // @cedarjs/testing's MockProviders.js + if (importer === 'cedarjs:/Routes.tsx' && id.startsWith('./')) { + return id + } + + return null + }, + } satisfies Plugin +} diff --git a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts new file mode 100644 index 0000000000..f63d996eef --- /dev/null +++ b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts @@ -0,0 +1,28 @@ +import fs from 'node:fs' + +import type { Plugin } from 'vite' + +import { getPaths } from '@cedarjs/project-config' + +export function mockProvidersRoutesPlugin() { + const routesPath = getPaths().web.routes + const routes = fs.readFileSync(routesPath, 'utf-8') + + return { + name: 'cedarjs:mock-providers-routes', + resolveId(id) { + if (id === 'cedarjs:/Routes.tsx') { + return id + } + + return null + }, + load(id) { + if (id === 'cedarjs:/Routes.tsx') { + return routes + } + + return null + }, + } satisfies Plugin +} diff --git a/packages/testing/src/vitest/vitest-config.ts b/packages/testing/src/vitest/vitest-config.ts new file mode 100644 index 0000000000..e1bbe50765 --- /dev/null +++ b/packages/testing/src/vitest/vitest-config.ts @@ -0,0 +1,83 @@ +import dns from 'node:dns' +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { getPaths } from '@cedarjs/project-config' + +import { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' +import { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' + +const rwjsPaths = getPaths() +const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules') + +// So that Vite will load on localhost instead of `127.0.0.1`. +// See: https://vitejs.dev/config/server-options.html#server-host. +dns.setDefaultResultOrder('verbatim') + +export default defineConfig(({ mode }) => ({ + plugins: [ + mode === 'test' && mockProvidersRoutesPlugin(), + mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), + ], + ssr: { + noExternal: ['@cedarjs/testing'], + }, + test: { + environment: 'jsdom', + // TODO: Set this to 'false', and let the user configure this on their own + // if this is something they want + // Enables global test APIs like describe, it, expect + globals: true, + // setupFiles: ['./vitest.setup.mjs'], + include: [path.join(rwjsPaths.web.src, '**/*.{test,spec}.{js,jsx,ts,tsx}')], + }, + + resolve: + mode === 'test' + ? { + alias: [ + // Do I need these? + // react: path.join(NODE_MODULES_PATH, 'react'), + // 'react-dom': path.join(NODE_MODULES_PATH, 'react-dom'), + + // Mock implementations + { + find: /^@cedarjs\/router$/, + replacement: path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/MockRouter.js', + ), + }, + // { + // find: '@cedarjs/web', + // replacement: path.join( + // NODE_MODULES_PATH, + // '@cedarjs/web/dist/cjs' + // ), + // }, + { + find: /^@cedarjs\/auth$/, + replacement: path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/mockAuth.js', + ), + }, + // // '@cedarjs/testing': path.join( + // '~__REDWOOD__USER_ROUTES_FOR_MOCK': { + // find: '', + // replacement: '', + // }, + // // '@cedarjs/testing': path.join( + // // NODE_MODULES_PATH, + // // '@cedarjs/testing/web' + // // ), + // '~__REDWOOD__USER_ROUTES_FOR_MOCK': path.join( + // __dirname, + // 'src', + // 'Routes.tsx' + // ), + ], + } + : {}, +})) From 7263385df8c985e765f19691582848e44b4d6e4f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 20:11:15 +0200 Subject: [PATCH 027/222] fix build --- package.json | 1 + .../mockProvidersRelativeRoutesPathsPlugin.ts | 4 +- .../src/vitest/mockProvidersRoutesPlugin.ts | 4 +- packages/testing/tsconfig.build.json | 4 +- packages/testing/tsconfig.cjs.json | 7 +- prettier.config.js | 9 +++ yarn.lock | 73 ++++--------------- 7 files changed, 36 insertions(+), 66 deletions(-) diff --git a/package.json b/package.json index 9af97e70f4..1bcaf041e8 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,7 @@ }, "resolutions": { "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", + "vite": "5.4.16", "vscode-languageserver": "6.1.1", "vscode-languageserver-protocol": "3.17.5", "vscode-languageserver-textdocument": "1.0.12", diff --git a/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts b/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts index 4fbc7aaabd..0b3b25ee90 100644 --- a/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts +++ b/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts @@ -1,6 +1,6 @@ import type { Plugin } from 'vite' -export function mockProvidersRelativeRoutesPathsPlugin() { +export function mockProvidersRelativeRoutesPathsPlugin(): Plugin { return { name: 'cedarjs:mock-providers-relative-routes-paths', resolveId(id, importer) { @@ -13,5 +13,5 @@ export function mockProvidersRelativeRoutesPathsPlugin() { return null }, - } satisfies Plugin + } } diff --git a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts index f63d996eef..4f1ae9b29f 100644 --- a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts +++ b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts @@ -4,7 +4,7 @@ import type { Plugin } from 'vite' import { getPaths } from '@cedarjs/project-config' -export function mockProvidersRoutesPlugin() { +export function mockProvidersRoutesPlugin(): Plugin { const routesPath = getPaths().web.routes const routes = fs.readFileSync(routesPath, 'utf-8') @@ -24,5 +24,5 @@ export function mockProvidersRoutesPlugin() { return null }, - } satisfies Plugin + } } diff --git a/packages/testing/tsconfig.build.json b/packages/testing/tsconfig.build.json index f4a758f7e1..ea41ce9c76 100644 --- a/packages/testing/tsconfig.build.json +++ b/packages/testing/tsconfig.build.json @@ -3,8 +3,8 @@ "compilerOptions": { "rootDir": "src", "outDir": "dist", - "module": "Node16", - "moduleResolution": "Node16", + "module": "NodeNext", + "moduleResolution": "NodeNext", "tsBuildInfoFile": "./tsconfig.build.tsbuildinfo" }, "include": ["src"], diff --git a/packages/testing/tsconfig.cjs.json b/packages/testing/tsconfig.cjs.json index a660cecf11..4a9ea99b92 100644 --- a/packages/testing/tsconfig.cjs.json +++ b/packages/testing/tsconfig.cjs.json @@ -3,5 +3,10 @@ "compilerOptions": { "outDir": "dist/cjs", "tsBuildInfoFile": "./tsconfig.cjs.tsbuildinfo" - } + }, + "exclude": [ + "**/__tests__", + // The vitest files are only used by ESM projects + "src/vitest" + ] } diff --git a/prettier.config.js b/prettier.config.js index 956faa5471..07f9aa0c91 100644 --- a/prettier.config.js +++ b/prettier.config.js @@ -15,6 +15,15 @@ const config = { 'prettier-plugin-sh', 'prettier-plugin-packagejson', ], + overrides: [ + { + files: ['tsconfig.cjs.json'], + options: { + parser: 'jsonc', + trailingComma: 'none', + }, + }, + ], } module.exports = config diff --git a/yarn.lock b/yarn.lock index 275bc22d20..a11fca3242 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3481,6 +3481,7 @@ __metadata: jest-watch-typeahead: "npm:2.2.2" jsdom: "npm:24.1.3" msw: "npm:1.3.4" + prettier: "npm:*" publint: "npm:0.3.12" ts-toolbelt: "npm:9.6.0" tsx: "npm:4.19.4" @@ -16766,7 +16767,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:^0.25.0, esbuild@npm:~0.25.0": +"esbuild@npm:~0.25.0": version: 0.25.5 resolution: "esbuild@npm:0.25.5" dependencies: @@ -17798,7 +17799,7 @@ __metadata: languageName: node linkType: hard -"fdir@npm:^6.2.0, fdir@npm:^6.4.4, fdir@npm:^6.4.6": +"fdir@npm:^6.2.0, fdir@npm:^6.4.4": version: 6.4.6 resolution: "fdir@npm:6.4.6" peerDependencies: @@ -25333,7 +25334,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.43, postcss@npm:^8.5.6": +"postcss@npm:^8.4.43": version: 8.5.6 resolution: "postcss@npm:8.5.6" dependencies: @@ -25400,6 +25401,15 @@ __metadata: languageName: node linkType: hard +"prettier@npm:*": + version: 3.6.2 + resolution: "prettier@npm:3.6.2" + bin: + prettier: bin/prettier.cjs + checksum: 10c0/488cb2f2b99ec13da1e50074912870217c11edaddedeadc649b1244c749d15ba94e846423d062e2c4c9ae683e2d65f754de28889ba06e697ac4f988d44f45812 + languageName: node + linkType: hard + "prettier@npm:3.5.3": version: 3.5.3 resolution: "prettier@npm:3.5.3" @@ -27046,7 +27056,7 @@ __metadata: languageName: node linkType: hard -"rollup@npm:^4.20.0, rollup@npm:^4.40.0": +"rollup@npm:^4.20.0": version: 4.44.1 resolution: "rollup@npm:4.44.1" dependencies: @@ -30264,61 +30274,6 @@ __metadata: languageName: node linkType: hard -"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": - version: 7.0.0 - resolution: "vite@npm:7.0.0" - dependencies: - esbuild: "npm:^0.25.0" - fdir: "npm:^6.4.6" - fsevents: "npm:~2.3.3" - picomatch: "npm:^4.0.2" - postcss: "npm:^8.5.6" - rollup: "npm:^4.40.0" - tinyglobby: "npm:^0.2.14" - peerDependencies: - "@types/node": ^20.19.0 || >=22.12.0 - jiti: ">=1.21.0" - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: ">=0.54.8" - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - dependenciesMeta: - fsevents: - optional: true - peerDependenciesMeta: - "@types/node": - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - bin: - vite: bin/vite.js - checksum: 10c0/860838d223f877dd8e04bd2b8f33cf67a38706643bdf07e3153e2857d7c0d33c3ee94cea7e86e60937cc91b3793272912cc7af14565641476f814bd61b3a1374 - languageName: node - linkType: hard - "vitest@npm:3.2.4": version: 3.2.4 resolution: "vitest@npm:3.2.4" From 6f3403bb394fa8b77a6d6ff3db42ab2305e2173d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 20:43:11 +0200 Subject: [PATCH 028/222] virtual cedarjs:/Routes.tsx --- packages/testing/package.json | 4 ++ packages/testing/src/vitest/index.ts | 2 + packages/testing/src/web/MockProviders.tsx | 46 ++++----------- packages/testing/src/web/MockProvidersCjs.tsx | 58 +++++++++++++++++++ packages/vite/package.json | 1 + packages/vite/src/index.ts | 10 +++- packages/vite/tsconfig.build.json | 1 + packages/vite/tsconfig.json | 1 + yarn.lock | 11 +--- 9 files changed, 87 insertions(+), 47 deletions(-) create mode 100644 packages/testing/src/vitest/index.ts create mode 100644 packages/testing/src/web/MockProvidersCjs.tsx diff --git a/packages/testing/package.json b/packages/testing/package.json index 0e1afba9c0..6b6b8d972d 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -61,6 +61,10 @@ "default": "./dist/cjs/cache/index.js" } }, + "./vitest": { + "types": "./dist/vitest/index.d.ts", + "default": "./dist/vitest/index.js" + }, "./web": { "import": { "types": "./dist/web/index.d.ts", diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts new file mode 100644 index 0000000000..6327722ccd --- /dev/null +++ b/packages/testing/src/vitest/index.ts @@ -0,0 +1,2 @@ +export { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' +export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' diff --git a/packages/testing/src/web/MockProviders.tsx b/packages/testing/src/web/MockProviders.tsx index e480b43b94..eb8c9c2b85 100644 --- a/packages/testing/src/web/MockProviders.tsx +++ b/packages/testing/src/web/MockProviders.tsx @@ -1,9 +1,14 @@ -/** - * NOTE: This module should not contain any nodejs functionality, - * because it's also used by Storybook in the browser. - */ +// NOTE: This module should not contain any nodejs functionality, because it's +// used by Storybook in the browser. + import React from 'react' +// @ts-expect-error - This is a virtual module, it doesn't have types +// The virtual module imports the user's Routes from `./web/src/Routes.{tsx,jsx}`, +// we pass the `children` from the user's Routes to `./MockRouter.Router` +// so that we can populate the `routes object` in Storybook and tests. +import UserRoutes from 'cedarjs:/Routes.tsx' + import { LocationProvider } from '@cedarjs/router' import { RedwoodProvider } from '@cedarjs/web' import { RedwoodApolloProvider } from '@cedarjs/web/apollo' @@ -11,26 +16,8 @@ import { RedwoodApolloProvider } from '@cedarjs/web/apollo' import { useAuth } from './mockAuth.js' import { MockParamsProvider } from './MockParamsProvider.js' -// Import the user's Routes from `./web/src/Routes.{tsx,jsx}`, -// we pass the `children` from the user's Routes to `./MockRouter.Router` -// so that we can populate the `routes object` in Storybook and tests. -let UserRoutes: React.FC - -// we need to do this to avoid "Could not resolve "~__REDWOOD__USER_ROUTES_FOR_MOCK"" errors -try { - const userRoutesModule = require('~__REDWOOD__USER_ROUTES_FOR_MOCK') - UserRoutes = userRoutesModule.default -} catch (error) { - if (!isModuleNotFoundError(error, '~__REDWOOD__USER_ROUTES_FOR_MOCK')) { - // if it's not "MODULE_NOT_FOUND" it's more likely a user error. Let's - // surface that to help the user debug the issue. - console.warn(error) - } - - UserRoutes = () => <> -} - -// TODO(pc): see if there are props we want to allow to be passed into our mock provider (e.g. AuthProviderProps) +// TODO(pc): see if there are props we want to allow to be passed into our mock +// provider (e.g. AuthProviderProps) export const MockProviders: React.FunctionComponent<{ children: React.ReactNode }> = ({ children }) => { @@ -45,14 +32,3 @@ export const MockProviders: React.FunctionComponent<{ ) } - -function isModuleNotFoundError(error: unknown, module: string) { - return ( - !!error && - typeof error === 'object' && - 'code' in error && - error.code === 'MODULE_NOT_FOUND' && - 'moduleName' in error && - error.moduleName === module - ) -} diff --git a/packages/testing/src/web/MockProvidersCjs.tsx b/packages/testing/src/web/MockProvidersCjs.tsx new file mode 100644 index 0000000000..e480b43b94 --- /dev/null +++ b/packages/testing/src/web/MockProvidersCjs.tsx @@ -0,0 +1,58 @@ +/** + * NOTE: This module should not contain any nodejs functionality, + * because it's also used by Storybook in the browser. + */ +import React from 'react' + +import { LocationProvider } from '@cedarjs/router' +import { RedwoodProvider } from '@cedarjs/web' +import { RedwoodApolloProvider } from '@cedarjs/web/apollo' + +import { useAuth } from './mockAuth.js' +import { MockParamsProvider } from './MockParamsProvider.js' + +// Import the user's Routes from `./web/src/Routes.{tsx,jsx}`, +// we pass the `children` from the user's Routes to `./MockRouter.Router` +// so that we can populate the `routes object` in Storybook and tests. +let UserRoutes: React.FC + +// we need to do this to avoid "Could not resolve "~__REDWOOD__USER_ROUTES_FOR_MOCK"" errors +try { + const userRoutesModule = require('~__REDWOOD__USER_ROUTES_FOR_MOCK') + UserRoutes = userRoutesModule.default +} catch (error) { + if (!isModuleNotFoundError(error, '~__REDWOOD__USER_ROUTES_FOR_MOCK')) { + // if it's not "MODULE_NOT_FOUND" it's more likely a user error. Let's + // surface that to help the user debug the issue. + console.warn(error) + } + + UserRoutes = () => <> +} + +// TODO(pc): see if there are props we want to allow to be passed into our mock provider (e.g. AuthProviderProps) +export const MockProviders: React.FunctionComponent<{ + children: React.ReactNode +}> = ({ children }) => { + return ( + + + + + {children} + + + + ) +} + +function isModuleNotFoundError(error: unknown, module: string) { + return ( + !!error && + typeof error === 'object' && + 'code' in error && + error.code === 'MODULE_NOT_FOUND' && + 'moduleName' in error && + error.moduleName === module + ) +} diff --git a/packages/vite/package.json b/packages/vite/package.json index 06c9128638..ba7229bfd4 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -65,6 +65,7 @@ "@cedarjs/internal": "workspace:*", "@cedarjs/project-config": "workspace:*", "@cedarjs/server-store": "workspace:*", + "@cedarjs/testing": "workspace:*", "@cedarjs/web": "workspace:*", "@swc/core": "1.12.1", "@vitejs/plugin-react": "4.3.4", diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index f4d25123f0..60e1a1dbcc 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -1,8 +1,12 @@ import react from '@vitejs/plugin-react' -import type { PluginOption } from 'vite' +import type { ConfigEnv, PluginOption } from 'vite' import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' import { getConfig } from '@cedarjs/project-config' +import { + mockProvidersRoutesPlugin, + mockProvidersRelativeRoutesPathsPlugin, +} from '@cedarjs/testing/vitest' import { cedarCellTransform } from './plugins/vite-plugin-cedar-cell.js' import { cedarEntryInjectionPlugin } from './plugins/vite-plugin-cedar-entry-injection.js' @@ -27,7 +31,7 @@ export { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provi /** * Pre-configured vite plugin, with required config for CedarJS apps. */ -export function cedar(): PluginOption[] { +export function cedar({ mode }: ConfigEnv): PluginOption[] { const rwConfig = getConfig() const rscEnabled = rwConfig.experimental?.rsc?.enabled @@ -51,6 +55,8 @@ export function cedar(): PluginOption[] { } return [ + mode === 'test' && mockProvidersRoutesPlugin(), + mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), cedarNodePolyfills(), cedarHtmlEnvPlugin(), cedarEntryInjectionPlugin(), diff --git a/packages/vite/tsconfig.build.json b/packages/vite/tsconfig.build.json index 8f6ab83222..f3ebc1f96b 100644 --- a/packages/vite/tsconfig.build.json +++ b/packages/vite/tsconfig.build.json @@ -13,6 +13,7 @@ { "path": "../project-config" }, { "path": "../router/tsconfig.build.json" }, { "path": "../server-store" }, + { "path": "../testing/tsconfig.build.json" }, { "path": "../web/tsconfig.build.json" } ] } diff --git a/packages/vite/tsconfig.json b/packages/vite/tsconfig.json index 46c38cb87c..4ea9d2914c 100644 --- a/packages/vite/tsconfig.json +++ b/packages/vite/tsconfig.json @@ -15,6 +15,7 @@ { "path": "../project-config" }, { "path": "../router/tsconfig.build.json" }, { "path": "../server-store" }, + { "path": "../testing/tsconfig.build.json" }, { "path": "../web/tsconfig.build.json" } ] } diff --git a/yarn.lock b/yarn.lock index a11fca3242..c76ddc4f23 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3481,7 +3481,6 @@ __metadata: jest-watch-typeahead: "npm:2.2.2" jsdom: "npm:24.1.3" msw: "npm:1.3.4" - prettier: "npm:*" publint: "npm:0.3.12" ts-toolbelt: "npm:9.6.0" tsx: "npm:4.19.4" @@ -3519,6 +3518,7 @@ __metadata: "@cedarjs/internal": "workspace:*" "@cedarjs/project-config": "workspace:*" "@cedarjs/server-store": "workspace:*" + "@cedarjs/testing": "workspace:*" "@cedarjs/web": "workspace:*" "@hyrious/esbuild-plugin-commonjs": "npm:0.2.6" "@swc/core": "npm:1.12.1" @@ -25401,15 +25401,6 @@ __metadata: languageName: node linkType: hard -"prettier@npm:*": - version: 3.6.2 - resolution: "prettier@npm:3.6.2" - bin: - prettier: bin/prettier.cjs - checksum: 10c0/488cb2f2b99ec13da1e50074912870217c11edaddedeadc649b1244c749d15ba94e846423d062e2c4c9ae683e2d65f754de28889ba06e697ac4f988d44f45812 - languageName: node - linkType: hard - "prettier@npm:3.5.3": version: 3.5.3 resolution: "prettier@npm:3.5.3" From 4a380ac5a1634e582c3493b4c61722e48ea605d8 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 21:17:44 +0200 Subject: [PATCH 029/222] include test settings by default --- __fixtures__/test-project/web/vite.config.ts | 9 ++--- .../templates/js/web/vite.config.js | 9 ++--- .../templates/ts/web/vite.config.ts | 9 ++--- packages/vite/src/index.ts | 8 +++-- packages/vite/src/lib/getMergedConfig.ts | 33 ++++++++++++++++++- 5 files changed, 53 insertions(+), 15 deletions(-) diff --git a/__fixtures__/test-project/web/vite.config.ts b/__fixtures__/test-project/web/vite.config.ts index 93fdcfe9c6..a68f501c10 100644 --- a/__fixtures__/test-project/web/vite.config.ts +++ b/__fixtures__/test-project/web/vite.config.ts @@ -1,6 +1,6 @@ /// -import dns from 'dns' +import dns from 'node:dns' import { defineConfig } from 'vite' @@ -10,10 +10,11 @@ import { cedar } from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -export default defineConfig({ - plugins: [cedar()], +export default defineConfig(({ mode }) => ({ + plugins: [cedar({ mode })], test: { environment: 'jsdom', + // Enables global test APIs like describe, it, expect globals: true, }, -}) +})) diff --git a/packages/create-cedar-app/templates/js/web/vite.config.js b/packages/create-cedar-app/templates/js/web/vite.config.js index 93fdcfe9c6..a68f501c10 100644 --- a/packages/create-cedar-app/templates/js/web/vite.config.js +++ b/packages/create-cedar-app/templates/js/web/vite.config.js @@ -1,6 +1,6 @@ /// -import dns from 'dns' +import dns from 'node:dns' import { defineConfig } from 'vite' @@ -10,10 +10,11 @@ import { cedar } from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -export default defineConfig({ - plugins: [cedar()], +export default defineConfig(({ mode }) => ({ + plugins: [cedar({ mode })], test: { environment: 'jsdom', + // Enables global test APIs like describe, it, expect globals: true, }, -}) +})) diff --git a/packages/create-cedar-app/templates/ts/web/vite.config.ts b/packages/create-cedar-app/templates/ts/web/vite.config.ts index 93fdcfe9c6..a68f501c10 100644 --- a/packages/create-cedar-app/templates/ts/web/vite.config.ts +++ b/packages/create-cedar-app/templates/ts/web/vite.config.ts @@ -1,6 +1,6 @@ /// -import dns from 'dns' +import dns from 'node:dns' import { defineConfig } from 'vite' @@ -10,10 +10,11 @@ import { cedar } from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -export default defineConfig({ - plugins: [cedar()], +export default defineConfig(({ mode }) => ({ + plugins: [cedar({ mode })], test: { environment: 'jsdom', + // Enables global test APIs like describe, it, expect globals: true, }, -}) +})) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 60e1a1dbcc..3e8652ee80 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -1,5 +1,5 @@ import react from '@vitejs/plugin-react' -import type { ConfigEnv, PluginOption } from 'vite' +import type { PluginOption } from 'vite' import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' import { getConfig } from '@cedarjs/project-config' @@ -28,10 +28,14 @@ export { cedarTransformJsAsJsx } from './plugins/vite-plugin-jsx-loader.js' export { cedarMergedConfig } from './plugins/vite-plugin-merged-config.js' export { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provider.js' +type PluginOptions = { + mode: string | undefined +} + /** * Pre-configured vite plugin, with required config for CedarJS apps. */ -export function cedar({ mode }: ConfigEnv): PluginOption[] { +export function cedar({ mode }: PluginOptions): PluginOption[] { const rwConfig = getConfig() const rscEnabled = rwConfig.experimental?.rsc?.enabled diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index c61719035c..f3c20cc297 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -1,8 +1,8 @@ import path from 'node:path' import type { InputOption } from 'rollup' -import type { ConfigEnv, UserConfig } from 'vite' import { mergeConfig } from 'vite' +import type { ConfigEnv, UserConfig } from 'vitest/config' import type { Config, Paths } from '@cedarjs/project-config' import { getConfig, getPaths } from '@cedarjs/project-config' @@ -33,6 +33,8 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { apiPort = rwConfig.api.port } + const NODE_MODULES_PATH = path.join(rwPaths.base, 'node_modules') + const defaultRwViteConfig: UserConfig = { root: rwPaths.web.src, // @MARK: when we have these aliases, the warnings from the FE server go @@ -143,6 +145,35 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { }, }, }, + ssr: { + noExternal: env.mode == 'test' ? ['@cedarjs/testing'] : [], + }, + resolve: { + alias: + env.mode === 'test' + ? [ + // Mock implementations + { + find: /^@cedarjs\/router$/, + replacement: path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/MockRouter.js', + ), + }, + { + find: /^@cedarjs\/auth$/, + replacement: path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/mockAuth.js', + ), + }, + ] + : [], + }, + test: { + globals: false, + environment: 'jsdom', + }, } return mergeConfig(defaultRwViteConfig, userConfig) From 2dabca576ca44c6b3a428b90ea658672a54544ae Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 21:28:00 +0200 Subject: [PATCH 030/222] update test project fixture --- __fixtures__/test-project/api/jest.config.cjs | 1 - 1 file changed, 1 deletion(-) diff --git a/__fixtures__/test-project/api/jest.config.cjs b/__fixtures__/test-project/api/jest.config.cjs index 6646ef5637..d81d7f1d14 100644 --- a/__fixtures__/test-project/api/jest.config.cjs +++ b/__fixtures__/test-project/api/jest.config.cjs @@ -2,7 +2,6 @@ const config = { rootDir: '../', - preset: '@cedarjs/testing/config/jest/api', } module.exports = config From ed87de05c85312c01a20bba0e1440f9efd02c97f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sun, 13 Jul 2025 23:38:47 +0200 Subject: [PATCH 031/222] Run vitest --- packages/cli/src/commands/testHandler.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 98a2b76ed4..ec13a99b3a 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -148,11 +148,13 @@ export const handler = async ({ process.env.SKIP_DB_PUSH = '1' } + console.log('jestArgs', jestArgs) + // **NOTE** There is no official way to run Jest programmatically, // so we're running it via execa, since `jest.run()` is a bit unstable. // https://github.com/facebook/jest/issues/5048 const runCommand = async () => { - await execa('yarn jest', jestArgs, { + await execa('yarn vitest', ['run'], { cwd: rwjsPaths.base, shell: true, stdio: 'inherit', From 88a3b65ebed6bf7d1313df73d4026fde12d378a3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 09:27:11 +0200 Subject: [PATCH 032/222] vitest app root config --- __fixtures__/test-project/jest.config.cjs | 8 -------- .../create-cedar-app/templates/js/jest.config.cjs | 8 -------- .../create-cedar-app/templates/js/vitest.config.mjs | 7 +++++++ .../create-cedar-app/templates/ts/jest.config.cjs | 8 -------- .../create-cedar-app/templates/ts/vitest.config.ts | 7 +++++++ packages/create-cedar-app/tests/templates.test.ts | 12 +++++++----- 6 files changed, 21 insertions(+), 29 deletions(-) delete mode 100644 __fixtures__/test-project/jest.config.cjs delete mode 100644 packages/create-cedar-app/templates/js/jest.config.cjs create mode 100644 packages/create-cedar-app/templates/js/vitest.config.mjs delete mode 100644 packages/create-cedar-app/templates/ts/jest.config.cjs create mode 100644 packages/create-cedar-app/templates/ts/vitest.config.ts diff --git a/__fixtures__/test-project/jest.config.cjs b/__fixtures__/test-project/jest.config.cjs deleted file mode 100644 index c6b395cb76..0000000000 --- a/__fixtures__/test-project/jest.config.cjs +++ /dev/null @@ -1,8 +0,0 @@ -// 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/packages/create-cedar-app/templates/js/jest.config.cjs b/packages/create-cedar-app/templates/js/jest.config.cjs deleted file mode 100644 index c6b395cb76..0000000000 --- a/packages/create-cedar-app/templates/js/jest.config.cjs +++ /dev/null @@ -1,8 +0,0 @@ -// 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/packages/create-cedar-app/templates/js/vitest.config.mjs b/packages/create-cedar-app/templates/js/vitest.config.mjs new file mode 100644 index 0000000000..79b00e32e9 --- /dev/null +++ b/packages/create-cedar-app/templates/js/vitest.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: ['/{*,!(node_modules)/**/}/vite*'], + }, +}) diff --git a/packages/create-cedar-app/templates/ts/jest.config.cjs b/packages/create-cedar-app/templates/ts/jest.config.cjs deleted file mode 100644 index c6b395cb76..0000000000 --- a/packages/create-cedar-app/templates/ts/jest.config.cjs +++ /dev/null @@ -1,8 +0,0 @@ -// 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/packages/create-cedar-app/templates/ts/vitest.config.ts b/packages/create-cedar-app/templates/ts/vitest.config.ts new file mode 100644 index 0000000000..79b00e32e9 --- /dev/null +++ b/packages/create-cedar-app/templates/ts/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: ['/{*,!(node_modules)/**/}/vite*'], + }, +}) diff --git a/packages/create-cedar-app/tests/templates.test.ts b/packages/create-cedar-app/tests/templates.test.ts index 5ba067f8f0..44b413cfe7 100644 --- a/packages/create-cedar-app/tests/templates.test.ts +++ b/packages/create-cedar-app/tests/templates.test.ts @@ -49,7 +49,7 @@ describe('TS template', () => { "/api/tsconfig.json", "/gitignore.template", "/graphql.config.cjs", - "/jest.config.cjs", + "/vitest.config.ts", "/package.json", "/prettier.config.cjs", "/redwood.toml", @@ -131,7 +131,7 @@ describe('JS template', () => { "/api/src/services/.keep", "/gitignore.template", "/graphql.config.cjs", - "/jest.config.cjs", + "/vitest.config.mjs", "/package.json", "/prettier.config.cjs", "/redwood.toml", @@ -169,9 +169,11 @@ describe('JS template', () => { }) /** - * Used to get the directory structure of the CRWA templates for snapshot testing. + * Used to get the directory structure of the create-cedar-app templates for + * snapshot testing. * - * Between CI and our branch strategy, this function has to handle some edge cases: + * Between CI and our branch strategy, this function has to handle some edge + * cases: * * - the yarn lint edge case * @@ -182,7 +184,7 @@ describe('JS template', () => { * * - the yarn.lock edge case * - * When we release , we add lock files to the templates to speed up yarn install. + * When we release, we add lock files to the templates to speed up yarn install. * We remove these lock files after releasing. * But before we release, we run all our unit tests, so these test sees an extra file and fails. * While introduces a blind spot (if a lock file gets added, it won't be caught), that's the tradeoff we're making. From 1e5596871feba0be1de211962771560aabbb4424 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 09:54:00 +0200 Subject: [PATCH 033/222] fix(api): Fix SupportedVerifierTypes import --- packages/api/src/webhooks/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/api/src/webhooks/index.ts b/packages/api/src/webhooks/index.ts index cb2cf6ba56..803df8a626 100644 --- a/packages/api/src/webhooks/index.ts +++ b/packages/api/src/webhooks/index.ts @@ -3,7 +3,7 @@ import type { APIGatewayProxyEvent } from 'aws-lambda' import type { VerifyOptions, SupportedVerifierTypes, -} from '../auth/verifiers/index.js' +} from '../auth/verifiers/common.js' import { createVerifier, WebhookVerificationError, From a0297e463c86aaca1ab856cfbdad7c51929848aa Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 10:43:04 +0200 Subject: [PATCH 034/222] fix(api): Fix webhook exports --- packages/api/src/webhooks/index.ts | 9 +- packages/api/src/webhooks/webhooks.test.ts | 9 + .../api/src/webhooks/webhooks.type.test.ts | 255 ++++++++++++++++++ 3 files changed, 266 insertions(+), 7 deletions(-) create mode 100644 packages/api/src/webhooks/webhooks.type.test.ts diff --git a/packages/api/src/webhooks/index.ts b/packages/api/src/webhooks/index.ts index 803df8a626..01a9df2466 100644 --- a/packages/api/src/webhooks/index.ts +++ b/packages/api/src/webhooks/index.ts @@ -11,13 +11,8 @@ import { DEFAULT_TOLERANCE, } from '../auth/verifiers/index.js' -export { - VerifyOptions, - WebhookVerificationError, - DEFAULT_WEBHOOK_SECRET, - SupportedVerifierTypes, -} from '../auth/verifiers/index.js' - +export type { VerifyOptions, SupportedVerifierTypes } +export { WebhookVerificationError, DEFAULT_WEBHOOK_SECRET } export const DEFAULT_WEBHOOK_SIGNATURE_HEADER = 'RW-WEBHOOK-SIGNATURE' /** diff --git a/packages/api/src/webhooks/webhooks.test.ts b/packages/api/src/webhooks/webhooks.test.ts index 397d9d9324..9e3f571272 100644 --- a/packages/api/src/webhooks/webhooks.test.ts +++ b/packages/api/src/webhooks/webhooks.test.ts @@ -424,6 +424,9 @@ describe('webhooks', () => { const svix_id = event.headers['svix-id'] const svix_timestamp = event.headers['svix-timestamp'] + expectToBeDefined(svix_id) + expectToBeDefined(svix_timestamp) + const payload = `${svix_id}.${svix_timestamp}.${event.body}` const secret = 'whsec_MY_VOICE_IS_MY_PASSPORT_VERIFY_ME'.slice(6) @@ -446,6 +449,8 @@ describe('webhooks', () => { return signature } } + + throw new Error('Could not transform signature') }, eventTimestamp: parseInt(svix_timestamp, 10) * 1000, // One minute from the event's timestamp is within the default @@ -459,3 +464,7 @@ describe('webhooks', () => { }) }) }) + +export function expectToBeDefined(value: T | undefined): asserts value is T { + expect(value).toBeDefined() +} diff --git a/packages/api/src/webhooks/webhooks.type.test.ts b/packages/api/src/webhooks/webhooks.type.test.ts new file mode 100644 index 0000000000..effe8dbfc5 --- /dev/null +++ b/packages/api/src/webhooks/webhooks.type.test.ts @@ -0,0 +1,255 @@ +import type { APIGatewayProxyEvent } from 'aws-lambda' +import { describe, test, expectTypeOf } from 'vitest' + +import type { + WebhookVerificationError, + DEFAULT_WEBHOOK_SECRET, + DEFAULT_WEBHOOK_SIGNATURE_HEADER, + signatureFromEvent, + verifyEvent, + verifySignature, + signPayload, + VerifyOptions, + SupportedVerifierTypes, +} from './index.js' + +describe('webhooks type tests', () => { + describe('VerifyOptions type', () => { + test('should be an interface with optional properties', () => { + expectTypeOf().toEqualTypeOf<{ + signatureHeader?: string + signatureTransformer?: (signature: string) => string + currentTimestampOverride?: number + eventTimestamp?: number + tolerance?: number + issuer?: string + }>() + }) + + test('should allow empty object', () => { + expectTypeOf>().toExtend() + }) + + test('should allow partial objects', () => { + expectTypeOf<{ signatureHeader: string }>().toExtend() + expectTypeOf<{ tolerance: number }>().toExtend() + expectTypeOf<{ + signatureHeader: string + tolerance: number + }>().toExtend() + }) + + test('should enforce correct property types', () => { + expectTypeOf().toEqualTypeOf< + string | undefined + >() + expectTypeOf().toEqualTypeOf< + ((signature: string) => string) | undefined + >() + expectTypeOf().toEqualTypeOf< + number | undefined + >() + expectTypeOf().toEqualTypeOf< + number | undefined + >() + expectTypeOf().toEqualTypeOf< + number | undefined + >() + expectTypeOf().toEqualTypeOf< + string | undefined + >() + }) + }) + + describe('SupportedVerifierTypes type', () => { + test('should be a union of string literals', () => { + expectTypeOf().toEqualTypeOf< + | 'skipVerifier' + | 'secretKeyVerifier' + | 'sha1Verifier' + | 'sha256Verifier' + | 'base64Sha1Verifier' + | 'base64Sha256Verifier' + | 'timestampSchemeVerifier' + | 'jwtVerifier' + >() + }) + + test('should accept valid verifier types', () => { + expectTypeOf<'skipVerifier'>().toMatchTypeOf() + expectTypeOf<'secretKeyVerifier'>().toMatchTypeOf() + expectTypeOf<'sha1Verifier'>().toMatchTypeOf() + expectTypeOf<'sha256Verifier'>().toMatchTypeOf() + expectTypeOf<'base64Sha1Verifier'>().toMatchTypeOf() + expectTypeOf<'base64Sha256Verifier'>().toMatchTypeOf() + expectTypeOf<'timestampSchemeVerifier'>().toMatchTypeOf() + expectTypeOf<'jwtVerifier'>().toMatchTypeOf() + }) + }) + + describe('WebhookVerificationError class', () => { + test('should be a class constructor', () => { + expectTypeOf().toBeConstructibleWith() + expectTypeOf().toBeConstructibleWith('') + expectTypeOf().toBeConstructibleWith( + 'custom message', + ) + }) + + test('should extend Error', () => { + expectTypeOf< + InstanceType + >().toExtend() + }) + + test('should have Error properties', () => { + expectTypeOf< + InstanceType['message'] + >().toEqualTypeOf() + expectTypeOf< + InstanceType['name'] + >().toEqualTypeOf() + expectTypeOf< + InstanceType['stack'] + >().toEqualTypeOf() + }) + }) + + describe('DEFAULT_WEBHOOK_SECRET constant', () => { + test('should be a string', () => { + expectTypeOf().toExtend() + }) + + test('should be assignable to string', () => { + expectTypeOf().toExtend() + }) + }) + + describe('DEFAULT_WEBHOOK_SIGNATURE_HEADER constant', () => { + test('should be a string', () => { + expectTypeOf().toExtend() + }) + + test('should be assignable to string', () => { + expectTypeOf().toExtend() + }) + + test('should be the expected literal value', () => { + expectTypeOf().toExtend() + }) + }) + + describe('function type signatures', () => { + test('signatureFromEvent should have correct signature', () => { + expectTypeOf().toEqualTypeOf< + ({ + event, + signatureHeader, + }: { + event: APIGatewayProxyEvent + signatureHeader: string + }) => string + >() + }) + + test('verifyEvent should have correct signature', () => { + expectTypeOf().toEqualTypeOf< + ( + type: SupportedVerifierTypes, + args: { + event: APIGatewayProxyEvent + payload?: string + secret?: string + options?: VerifyOptions | undefined + }, + ) => boolean | WebhookVerificationError + >() + }) + + test('verifySignature should have correct signature', () => { + expectTypeOf().toEqualTypeOf< + ( + type: SupportedVerifierTypes, + args: { + payload: string | Record + secret: string + signature: string + options?: VerifyOptions | undefined + }, + ) => boolean | WebhookVerificationError + >() + }) + + test('signPayload should have correct signature', () => { + expectTypeOf().toEqualTypeOf< + ( + type: SupportedVerifierTypes, + args: { + payload: string + secret: string + options?: VerifyOptions | undefined + }, + ) => string + >() + }) + }) + + describe('function parameter compatibility', () => { + test('verifyEvent should accept valid parameters', () => { + const mockEvent = {} as APIGatewayProxyEvent + const validType: SupportedVerifierTypes = 'sha256Verifier' + const validOptions: VerifyOptions = { + signatureHeader: 'custom-header', + tolerance: 30000, + } + + expectTypeOf>().toEqualTypeOf< + [ + SupportedVerifierTypes, + { + event: APIGatewayProxyEvent + payload?: string + secret?: string + options?: VerifyOptions | undefined + }, + ] + >() + + // Test that function calls would be type-safe + expectTypeOf().toBeCallableWith(validType, { + event: mockEvent, + secret: 'test-secret', + options: validOptions, + }) + }) + + test('verifySignature should accept valid parameters', () => { + const validType: SupportedVerifierTypes = 'sha1Verifier' + const validOptions: VerifyOptions = { tolerance: 5000 } + + expectTypeOf().toBeCallableWith(validType, { + payload: 'test payload', + secret: 'test-secret', + signature: 'test-signature', + options: validOptions, + }) + + expectTypeOf().toBeCallableWith(validType, { + payload: { data: 'test' }, + secret: 'test-secret', + signature: 'test-signature', + }) + }) + + test('signPayload should accept valid parameters', () => { + const validType: SupportedVerifierTypes = 'jwtVerifier' + const validOptions: VerifyOptions = { issuer: 'test-issuer' } + + expectTypeOf().toBeCallableWith(validType, { + payload: 'test payload', + secret: 'test-secret', + options: validOptions, + }) + }) + }) +}) From eb5f4621c3f54f38c314922bcd51b2512f3e7ae3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 13:36:53 +0200 Subject: [PATCH 035/222] vitest api config --- __fixtures__/test-project/api/jest.config.cjs | 7 ------- __fixtures__/test-project/api/vitest.config.ts | 18 ++++++++++++++++++ __fixtures__/test-project/vitest.config.mjs | 7 +++++++ __fixtures__/test-project/web/jest.config.cjs | 7 ------- .../templates/js/api/jest.config.cjs | 7 ------- .../templates/js/api/vitest.config.js | 18 ++++++++++++++++++ .../templates/ts/api/jest.config.cjs | 7 ------- .../templates/ts/api/vitest.config.ts | 18 ++++++++++++++++++ 8 files changed, 61 insertions(+), 28 deletions(-) delete mode 100644 __fixtures__/test-project/api/jest.config.cjs create mode 100644 __fixtures__/test-project/api/vitest.config.ts create mode 100644 __fixtures__/test-project/vitest.config.mjs delete mode 100644 __fixtures__/test-project/web/jest.config.cjs delete mode 100644 packages/create-cedar-app/templates/js/api/jest.config.cjs create mode 100644 packages/create-cedar-app/templates/js/api/vitest.config.js delete mode 100644 packages/create-cedar-app/templates/ts/api/jest.config.cjs create mode 100644 packages/create-cedar-app/templates/ts/api/vitest.config.ts diff --git a/__fixtures__/test-project/api/jest.config.cjs b/__fixtures__/test-project/api/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/__fixtures__/test-project/api/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/__fixtures__/test-project/api/vitest.config.ts b/__fixtures__/test-project/api/vitest.config.ts new file mode 100644 index 0000000000..bac6839c94 --- /dev/null +++ b/__fixtures__/test-project/api/vitest.config.ts @@ -0,0 +1,18 @@ +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' + +export default defineConfig({ + plugins: [cedarjsDirectoryNamedImportPlugin()], + resolve: { + alias: { + src: path.resolve(__dirname, './src'), + }, + }, + test: { + fileParallelism: false, + globals: true, + }, +}) diff --git a/__fixtures__/test-project/vitest.config.mjs b/__fixtures__/test-project/vitest.config.mjs new file mode 100644 index 0000000000..79b00e32e9 --- /dev/null +++ b/__fixtures__/test-project/vitest.config.mjs @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: ['/{*,!(node_modules)/**/}/vite*'], + }, +}) diff --git a/__fixtures__/test-project/web/jest.config.cjs b/__fixtures__/test-project/web/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/__fixtures__/test-project/web/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/packages/create-cedar-app/templates/js/api/jest.config.cjs b/packages/create-cedar-app/templates/js/api/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/packages/create-cedar-app/templates/js/api/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/packages/create-cedar-app/templates/js/api/vitest.config.js b/packages/create-cedar-app/templates/js/api/vitest.config.js new file mode 100644 index 0000000000..bac6839c94 --- /dev/null +++ b/packages/create-cedar-app/templates/js/api/vitest.config.js @@ -0,0 +1,18 @@ +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' + +export default defineConfig({ + plugins: [cedarjsDirectoryNamedImportPlugin()], + resolve: { + alias: { + src: path.resolve(__dirname, './src'), + }, + }, + test: { + fileParallelism: false, + globals: true, + }, +}) diff --git a/packages/create-cedar-app/templates/ts/api/jest.config.cjs b/packages/create-cedar-app/templates/ts/api/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/packages/create-cedar-app/templates/ts/api/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/packages/create-cedar-app/templates/ts/api/vitest.config.ts b/packages/create-cedar-app/templates/ts/api/vitest.config.ts new file mode 100644 index 0000000000..bac6839c94 --- /dev/null +++ b/packages/create-cedar-app/templates/ts/api/vitest.config.ts @@ -0,0 +1,18 @@ +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' + +export default defineConfig({ + plugins: [cedarjsDirectoryNamedImportPlugin()], + resolve: { + alias: { + src: path.resolve(__dirname, './src'), + }, + }, + test: { + fileParallelism: false, + globals: true, + }, +}) From dc6be5cde757bf12846bc2f7358e3d31a1932f74 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 13:57:22 +0200 Subject: [PATCH 036/222] update create-cedar-app templates and tests --- .../create-cedar-app/templates/js/web/jest.config.cjs | 7 ------- .../create-cedar-app/templates/ts/web/jest.config.cjs | 7 ------- packages/create-cedar-app/tests/templates.test.ts | 10 ++++------ 3 files changed, 4 insertions(+), 20 deletions(-) delete mode 100644 packages/create-cedar-app/templates/js/web/jest.config.cjs delete mode 100644 packages/create-cedar-app/templates/ts/web/jest.config.cjs diff --git a/packages/create-cedar-app/templates/js/web/jest.config.cjs b/packages/create-cedar-app/templates/js/web/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/packages/create-cedar-app/templates/js/web/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/packages/create-cedar-app/templates/ts/web/jest.config.cjs b/packages/create-cedar-app/templates/ts/web/jest.config.cjs deleted file mode 100644 index d81d7f1d14..0000000000 --- a/packages/create-cedar-app/templates/ts/web/jest.config.cjs +++ /dev/null @@ -1,7 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', -} - -module.exports = config diff --git a/packages/create-cedar-app/tests/templates.test.ts b/packages/create-cedar-app/tests/templates.test.ts index 44b413cfe7..080f26334b 100644 --- a/packages/create-cedar-app/tests/templates.test.ts +++ b/packages/create-cedar-app/tests/templates.test.ts @@ -26,7 +26,6 @@ describe('TS template', () => { "/api", "/api/db", "/api/db/schema.prisma", - "/api/jest.config.cjs", "/api/package.json", "/api/src", "/api/src/directives", @@ -47,9 +46,9 @@ describe('TS template', () => { "/api/src/services", "/api/src/services/.keep", "/api/tsconfig.json", + "/api/vitest.config.ts", "/gitignore.template", "/graphql.config.cjs", - "/vitest.config.ts", "/package.json", "/prettier.config.cjs", "/redwood.toml", @@ -57,8 +56,8 @@ describe('TS template', () => { "/scripts/.keep", "/scripts/seed.ts", "/scripts/tsconfig.json", + "/vitest.config.ts", "/web", - "/web/jest.config.cjs", "/web/package.json", "/web/public", "/web/public/README.md", @@ -108,7 +107,6 @@ describe('JS template', () => { "/api", "/api/db", "/api/db/schema.prisma", - "/api/jest.config.cjs", "/api/jsconfig.json", "/api/package.json", "/api/src", @@ -129,9 +127,9 @@ describe('JS template', () => { "/api/src/lib/logger.js", "/api/src/services", "/api/src/services/.keep", + "/api/vitest.config.js", "/gitignore.template", "/graphql.config.cjs", - "/vitest.config.mjs", "/package.json", "/prettier.config.cjs", "/redwood.toml", @@ -139,8 +137,8 @@ describe('JS template', () => { "/scripts/.keep", "/scripts/jsconfig.json", "/scripts/seed.js", + "/vitest.config.mjs", "/web", - "/web/jest.config.cjs", "/web/jsconfig.json", "/web/package.json", "/web/public", From 50af3ff8840c11bdf27ed1c283499f0f4c81fa88 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 14:01:17 +0200 Subject: [PATCH 037/222] update test project fixture --- __fixtures__/test-project/{vitest.config.mjs => vitest.config.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename __fixtures__/test-project/{vitest.config.mjs => vitest.config.ts} (100%) diff --git a/__fixtures__/test-project/vitest.config.mjs b/__fixtures__/test-project/vitest.config.ts similarity index 100% rename from __fixtures__/test-project/vitest.config.mjs rename to __fixtures__/test-project/vitest.config.ts From c8588eaf9f38a4ed6b5616efe21668ca5da6ca45 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 15:33:59 +0200 Subject: [PATCH 038/222] feat(cli): Tailwind and PostCSS .cjs --- .../src/commands/setup/ui/libraries/tailwindcssHandler.js | 6 +++--- ...stcss.config.js.template => postcss.config.cjs.template} | 2 +- packages/project-config/src/paths.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename packages/cli/src/commands/setup/ui/templates/{postcss.config.js.template => postcss.config.cjs.template} (94%) diff --git a/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js b/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js index abb5dd4d47..58bd2527da 100644 --- a/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js +++ b/packages/cli/src/commands/setup/ui/libraries/tailwindcssHandler.js @@ -183,7 +183,7 @@ export const handler = async ({ force, install }) => { const postCSSConfig = fs.readFileSync( path.join( import.meta.dirname, - '../templates/postcss.config.js.template', + '../templates/postcss.config.cjs.template', ), 'utf-8', ) @@ -199,7 +199,7 @@ export const handler = async ({ force, install }) => { task: async () => { const tailwindConfigPath = path.join( rwPaths.web.config, - 'tailwind.config.js', + 'tailwind.config.cjs', ) if (fs.existsSync(tailwindConfigPath)) { @@ -388,7 +388,7 @@ export const handler = async ({ force, install }) => { const tailwindConfigPath = path .relative( rwPaths.base, - path.posix.join(rwPaths.web.config, 'tailwind.config.js'), + path.posix.join(rwPaths.web.config, 'tailwind.config.cjs'), ) .replaceAll('\\', '/') diff --git a/packages/cli/src/commands/setup/ui/templates/postcss.config.js.template b/packages/cli/src/commands/setup/ui/templates/postcss.config.cjs.template similarity index 94% rename from packages/cli/src/commands/setup/ui/templates/postcss.config.js.template rename to packages/cli/src/commands/setup/ui/templates/postcss.config.cjs.template index 3cdce19941..f6d96e144e 100644 --- a/packages/cli/src/commands/setup/ui/templates/postcss.config.js.template +++ b/packages/cli/src/commands/setup/ui/templates/postcss.config.cjs.template @@ -3,7 +3,7 @@ const path = require('path') module.exports = { plugins: [ require('tailwindcss/nesting'), - require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')), + require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.cjs')), require('autoprefixer'), ], } diff --git a/packages/project-config/src/paths.ts b/packages/project-config/src/paths.ts index 65e20c98ac..33d5c420c1 100644 --- a/packages/project-config/src/paths.ts +++ b/packages/project-config/src/paths.ts @@ -122,7 +122,7 @@ const PATH_WEB_DIR_ENTRY_CLIENT = 'web/src/entry.client' // .jsx,.tsx const PATH_WEB_DIR_ENTRY_SERVER = 'web/src/entry.server' // .jsx,.tsx const PATH_WEB_DIR_GRAPHQL = 'web/src/graphql' // .js,.ts -const PATH_WEB_DIR_CONFIG_POSTCSS = 'web/config/postcss.config.js' +const PATH_WEB_DIR_CONFIG_POSTCSS = 'web/config/postcss.config.cjs' const PATH_WEB_DIR_CONFIG_STORYBOOK_CONFIG = 'web/.storybook/main.js' const PATH_WEB_DIR_CONFIG_STORYBOOK_PREVIEW = 'web/.storybook/preview' // .js, .tsx const PATH_WEB_DIR_CONFIG_STORYBOOK_MANAGER = 'web/.storybook/manager.js' From e42252c837fbe62297bd493b177c2663e067060c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 15:49:54 +0200 Subject: [PATCH 039/222] templates: set "type": "module" by default --- __fixtures__/test-project/api/package.json | 1 + __fixtures__/test-project/package.json | 3 ++- __fixtures__/test-project/web/package.json | 1 + packages/create-cedar-app/templates/js/api/package.json | 1 + packages/create-cedar-app/templates/js/package.json | 1 + packages/create-cedar-app/templates/js/web/package.json | 1 + packages/create-cedar-app/templates/ts/api/package.json | 1 + packages/create-cedar-app/templates/ts/package.json | 1 + packages/create-cedar-app/templates/ts/web/package.json | 1 + 9 files changed, 10 insertions(+), 1 deletion(-) diff --git a/__fixtures__/test-project/api/package.json b/__fixtures__/test-project/api/package.json index b875503e83..f3b9e4e1cb 100644 --- a/__fixtures__/test-project/api/package.json +++ b/__fixtures__/test-project/api/package.json @@ -1,5 +1,6 @@ { "name": "api", + "type": "module", "version": "0.0.0", "private": true, "dependencies": { diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index dc5914a840..600c17d474 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "workspaces": { "packages": [ "api", @@ -27,4 +28,4 @@ "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", "react-is": "19.0.0-rc-f2df5694-20240916" } -} \ No newline at end of file +} diff --git a/__fixtures__/test-project/web/package.json b/__fixtures__/test-project/web/package.json index 19fa0974f2..63050afa37 100644 --- a/__fixtures__/test-project/web/package.json +++ b/__fixtures__/test-project/web/package.json @@ -1,5 +1,6 @@ { "name": "web", + "type": "module", "version": "0.0.0", "private": true, "browserslist": { diff --git a/packages/create-cedar-app/templates/js/api/package.json b/packages/create-cedar-app/templates/js/api/package.json index e7bd1a5d90..f5f58e869c 100644 --- a/packages/create-cedar-app/templates/js/api/package.json +++ b/packages/create-cedar-app/templates/js/api/package.json @@ -1,5 +1,6 @@ { "name": "api", + "type": "module", "version": "0.0.0", "private": true, "dependencies": { diff --git a/packages/create-cedar-app/templates/js/package.json b/packages/create-cedar-app/templates/js/package.json index 75ca3aca49..a6ea5deab0 100644 --- a/packages/create-cedar-app/templates/js/package.json +++ b/packages/create-cedar-app/templates/js/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "workspaces": { "packages": [ "api", diff --git a/packages/create-cedar-app/templates/js/web/package.json b/packages/create-cedar-app/templates/js/web/package.json index a5ea29d250..23c0584006 100644 --- a/packages/create-cedar-app/templates/js/web/package.json +++ b/packages/create-cedar-app/templates/js/web/package.json @@ -1,5 +1,6 @@ { "name": "web", + "type": "module", "version": "0.0.0", "private": true, "browserslist": { diff --git a/packages/create-cedar-app/templates/ts/api/package.json b/packages/create-cedar-app/templates/ts/api/package.json index e7bd1a5d90..f5f58e869c 100644 --- a/packages/create-cedar-app/templates/ts/api/package.json +++ b/packages/create-cedar-app/templates/ts/api/package.json @@ -1,5 +1,6 @@ { "name": "api", + "type": "module", "version": "0.0.0", "private": true, "dependencies": { diff --git a/packages/create-cedar-app/templates/ts/package.json b/packages/create-cedar-app/templates/ts/package.json index 75ca3aca49..a6ea5deab0 100644 --- a/packages/create-cedar-app/templates/ts/package.json +++ b/packages/create-cedar-app/templates/ts/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "workspaces": { "packages": [ "api", diff --git a/packages/create-cedar-app/templates/ts/web/package.json b/packages/create-cedar-app/templates/ts/web/package.json index a5ea29d250..23c0584006 100644 --- a/packages/create-cedar-app/templates/ts/web/package.json +++ b/packages/create-cedar-app/templates/ts/web/package.json @@ -1,5 +1,6 @@ { "name": "web", + "type": "module", "version": "0.0.0", "private": true, "browserslist": { From 704e8c646a3e9214ce71cf1ee758176cea8fe914 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 15:54:22 +0200 Subject: [PATCH 040/222] .js imports for dbAuth setup --- __fixtures__/test-project/api/src/functions/auth.ts | 4 ++-- __fixtures__/test-project/api/src/lib/auth.ts | 2 +- .../dbAuth/setup/src/templates/api/functions/auth.ts.template | 4 ++-- .../src/templates/api/functions/auth.webAuthn.ts.template | 4 ++-- .../dbAuth/setup/src/templates/api/lib/auth.ts.template | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/__fixtures__/test-project/api/src/functions/auth.ts b/__fixtures__/test-project/api/src/functions/auth.ts index 149164b1c5..b2192a0dcf 100644 --- a/__fixtures__/test-project/api/src/functions/auth.ts +++ b/__fixtures__/test-project/api/src/functions/auth.ts @@ -3,8 +3,8 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda' import { DbAuthHandler } from '@cedarjs/auth-dbauth-api' import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api' -import { cookieName } from 'src/lib/auth' -import { db } from 'src/lib/db' +import { cookieName } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' export const handler = async ( event: APIGatewayProxyEvent, diff --git a/__fixtures__/test-project/api/src/lib/auth.ts b/__fixtures__/test-project/api/src/lib/auth.ts index 90e6400ed9..e7eafb1ef6 100644 --- a/__fixtures__/test-project/api/src/lib/auth.ts +++ b/__fixtures__/test-project/api/src/lib/auth.ts @@ -1,7 +1,7 @@ import type { Decoded } from '@cedarjs/api' import { AuthenticationError, ForbiddenError } from '@cedarjs/graphql-server' -import { db } from './db' +import { db } from './db.js' /** * The name of the cookie that dbAuth sets diff --git a/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.ts.template b/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.ts.template index 4a7c9998eb..28033dcd55 100644 --- a/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.ts.template +++ b/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.ts.template @@ -3,8 +3,8 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda' import { DbAuthHandler } from '@cedarjs/auth-dbauth-api' import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api' -import { cookieName } from 'src/lib/auth' -import { db } from 'src/lib/db' +import { cookieName } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' export const handler = async ( event: APIGatewayProxyEvent, diff --git a/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.webAuthn.ts.template b/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.webAuthn.ts.template index 979e4c2efa..3f519572c5 100644 --- a/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.webAuthn.ts.template +++ b/packages/auth-providers/dbAuth/setup/src/templates/api/functions/auth.webAuthn.ts.template @@ -3,8 +3,8 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda' import { DbAuthHandler } from '@cedarjs/auth-dbauth-api' import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api' -import { cookieName } from 'src/lib/auth' -import { db } from 'src/lib/db' +import { cookieName } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' export const handler = async ( event: APIGatewayProxyEvent, diff --git a/packages/auth-providers/dbAuth/setup/src/templates/api/lib/auth.ts.template b/packages/auth-providers/dbAuth/setup/src/templates/api/lib/auth.ts.template index a3c4b57aeb..c0842dc1aa 100644 --- a/packages/auth-providers/dbAuth/setup/src/templates/api/lib/auth.ts.template +++ b/packages/auth-providers/dbAuth/setup/src/templates/api/lib/auth.ts.template @@ -1,7 +1,7 @@ import type { Decoded } from '@cedarjs/api' import { AuthenticationError, ForbiddenError } from '@cedarjs/graphql-server' -import { db } from './db' +import { db } from './db.js' /** * The name of the cookie that dbAuth sets From 7bd71df27771a613aa0a05622c5deef332febc9f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 20:27:44 +0200 Subject: [PATCH 041/222] service template: .js extension in relative import --- .../src/commands/generate/service/templates/service.ts.template | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/generate/service/templates/service.ts.template b/packages/cli/src/commands/generate/service/templates/service.ts.template index cbd5ddd312..c900fb4527 100644 --- a/packages/cli/src/commands/generate/service/templates/service.ts.template +++ b/packages/cli/src/commands/generate/service/templates/service.ts.template @@ -1,6 +1,6 @@ import type { QueryResolvers<% if (crud) { %>, MutationResolvers<% } %><% if (relations.length) { %>, ${singularPascalName}RelationResolvers<% } %> } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const ${pluralCamelName}: QueryResolvers['${pluralCamelName}'] = () => { return db.${singularCamelName}.findMany() From c45a4bad34e411e74a7e7565178967e3d0a421df Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 14 Jul 2025 20:28:07 +0200 Subject: [PATCH 042/222] vitest.setup.ts --- __fixtures__/test-project/web/vite.config.ts | 1 + __fixtures__/test-project/web/vitest.setup.ts | 12 ++++++++++++ .../create-cedar-app/templates/js/web/vite.config.js | 1 + .../templates/js/web/vitest.setup.js | 12 ++++++++++++ .../create-cedar-app/templates/ts/web/vite.config.ts | 1 + .../templates/ts/web/vitest.setup.ts | 12 ++++++++++++ packages/create-cedar-app/tests/templates.test.ts | 2 ++ 7 files changed, 41 insertions(+) create mode 100644 __fixtures__/test-project/web/vitest.setup.ts create mode 100644 packages/create-cedar-app/templates/js/web/vitest.setup.js create mode 100644 packages/create-cedar-app/templates/ts/web/vitest.setup.ts diff --git a/__fixtures__/test-project/web/vite.config.ts b/__fixtures__/test-project/web/vite.config.ts index a68f501c10..8db6f5be72 100644 --- a/__fixtures__/test-project/web/vite.config.ts +++ b/__fixtures__/test-project/web/vite.config.ts @@ -14,6 +14,7 @@ export default defineConfig(({ mode }) => ({ plugins: [cedar({ mode })], test: { environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], // Enables global test APIs like describe, it, expect globals: true, }, diff --git a/__fixtures__/test-project/web/vitest.setup.ts b/__fixtures__/test-project/web/vitest.setup.ts new file mode 100644 index 0000000000..3c2a688f47 --- /dev/null +++ b/__fixtures__/test-project/web/vitest.setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom/vitest' + +import { cleanup } from '@testing-library/react' +import { afterEach } from 'vitest' + +afterEach(() => { + // If vitest globals are enabled testing-library will clean up after each + // test automatically, but we don't enable globals, so we have to manually + // clean up here + // https://testing-library.com/docs/react-testing-library/api/#cleanup + cleanup() +}) diff --git a/packages/create-cedar-app/templates/js/web/vite.config.js b/packages/create-cedar-app/templates/js/web/vite.config.js index a68f501c10..8a7aeb6513 100644 --- a/packages/create-cedar-app/templates/js/web/vite.config.js +++ b/packages/create-cedar-app/templates/js/web/vite.config.js @@ -14,6 +14,7 @@ export default defineConfig(({ mode }) => ({ plugins: [cedar({ mode })], test: { environment: 'jsdom', + setupFiles: ['./vitest.setup.js'], // Enables global test APIs like describe, it, expect globals: true, }, diff --git a/packages/create-cedar-app/templates/js/web/vitest.setup.js b/packages/create-cedar-app/templates/js/web/vitest.setup.js new file mode 100644 index 0000000000..3c2a688f47 --- /dev/null +++ b/packages/create-cedar-app/templates/js/web/vitest.setup.js @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom/vitest' + +import { cleanup } from '@testing-library/react' +import { afterEach } from 'vitest' + +afterEach(() => { + // If vitest globals are enabled testing-library will clean up after each + // test automatically, but we don't enable globals, so we have to manually + // clean up here + // https://testing-library.com/docs/react-testing-library/api/#cleanup + cleanup() +}) diff --git a/packages/create-cedar-app/templates/ts/web/vite.config.ts b/packages/create-cedar-app/templates/ts/web/vite.config.ts index a68f501c10..8db6f5be72 100644 --- a/packages/create-cedar-app/templates/ts/web/vite.config.ts +++ b/packages/create-cedar-app/templates/ts/web/vite.config.ts @@ -14,6 +14,7 @@ export default defineConfig(({ mode }) => ({ plugins: [cedar({ mode })], test: { environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], // Enables global test APIs like describe, it, expect globals: true, }, diff --git a/packages/create-cedar-app/templates/ts/web/vitest.setup.ts b/packages/create-cedar-app/templates/ts/web/vitest.setup.ts new file mode 100644 index 0000000000..3c2a688f47 --- /dev/null +++ b/packages/create-cedar-app/templates/ts/web/vitest.setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom/vitest' + +import { cleanup } from '@testing-library/react' +import { afterEach } from 'vitest' + +afterEach(() => { + // If vitest globals are enabled testing-library will clean up after each + // test automatically, but we don't enable globals, so we have to manually + // clean up here + // https://testing-library.com/docs/react-testing-library/api/#cleanup + cleanup() +}) diff --git a/packages/create-cedar-app/tests/templates.test.ts b/packages/create-cedar-app/tests/templates.test.ts index 080f26334b..40d430f94e 100644 --- a/packages/create-cedar-app/tests/templates.test.ts +++ b/packages/create-cedar-app/tests/templates.test.ts @@ -57,6 +57,7 @@ describe('TS template', () => { "/scripts/seed.ts", "/scripts/tsconfig.json", "/vitest.config.ts", + "/vitest.setup.ts", "/web", "/web/package.json", "/web/public", @@ -138,6 +139,7 @@ describe('JS template', () => { "/scripts/jsconfig.json", "/scripts/seed.js", "/vitest.config.mjs", + "/vitest.setup.js", "/web", "/web/jsconfig.json", "/web/package.json", From bd5981e678fae42b6a56f79b2a50ce75c8c32eb3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 15 Jul 2025 05:11:46 +0200 Subject: [PATCH 043/222] hacky @cedarjs/auth resolve alias --- packages/vite/src/lib/getMergedConfig.ts | 25 +++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index f3c20cc297..2a2209790e 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -164,8 +164,31 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { find: /^@cedarjs\/auth$/, replacement: path.join( NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/mockAuth.js', + '@cedarjs/auth/dist/index.js', ), + customResolver: (id, importer) => { + // console.log('getMergedConfig auth customResolver id', id) + console.log( + 'getMergedConfig auth customResolver importer', + importer, + ) + + if (importer?.endsWith('mockAuth.js')) { + console.log('returning id', id) + return id + } + + const mockAuthId = path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/mockAuth.js', + ) + console.log('returning id', mockAuthId) + + return path.join( + NODE_MODULES_PATH, + '@cedarjs/testing/dist/web/mockAuth.js', + ) + }, }, ] : [], From 58f08039182188b3c1db7b60ace70215bf79d36c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 18 Jul 2025 15:50:38 +0200 Subject: [PATCH 044/222] fix create-cedar-app test --- packages/create-cedar-app/tests/templates.test.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/create-cedar-app/tests/templates.test.ts b/packages/create-cedar-app/tests/templates.test.ts index 40d430f94e..cc331ce633 100644 --- a/packages/create-cedar-app/tests/templates.test.ts +++ b/packages/create-cedar-app/tests/templates.test.ts @@ -57,7 +57,6 @@ describe('TS template', () => { "/scripts/seed.ts", "/scripts/tsconfig.json", "/vitest.config.ts", - "/vitest.setup.ts", "/web", "/web/package.json", "/web/public", @@ -81,6 +80,7 @@ describe('TS template', () => { "/web/src/pages/NotFoundPage/NotFoundPage.tsx", "/web/tsconfig.json", "/web/vite.config.ts", + "/web/vitest.setup.ts", ] `) }) @@ -139,7 +139,6 @@ describe('JS template', () => { "/scripts/jsconfig.json", "/scripts/seed.js", "/vitest.config.mjs", - "/vitest.setup.js", "/web", "/web/jsconfig.json", "/web/package.json", @@ -163,6 +162,7 @@ describe('JS template', () => { "/web/src/pages/NotFoundPage", "/web/src/pages/NotFoundPage/NotFoundPage.jsx", "/web/vite.config.js", + "/web/vitest.setup.js", ] `) }) From 3d80f105aea5e7b7708e2a765bcd503a9803b21a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 18 Jul 2025 15:54:31 +0200 Subject: [PATCH 045/222] fix test.test.js and update snapshots --- .../cli/src/commands/__tests__/test.test.js | 18 +++++++------- .../__tests__/__snapshots__/sdl.test.js.snap | 8 +++---- .../__snapshots__/service.test.js.snap | 24 +++++++++---------- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/packages/cli/src/commands/__tests__/test.test.js b/packages/cli/src/commands/__tests__/test.test.js index 32de9a94af..bc19a4693d 100644 --- a/packages/cli/src/commands/__tests__/test.test.js +++ b/packages/cli/src/commands/__tests__/test.test.js @@ -39,7 +39,7 @@ afterEach(() => { test('Runs tests for all available sides if no filter passed', async () => { await handler({}) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('web') expect(execa.mock.results[0].value.params).toContain('api') }) @@ -50,7 +50,7 @@ test('Syncs or creates test database when the flag --db-push is set to true', as dbPush: true, }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('--projects', 'api') }) @@ -61,7 +61,7 @@ test('Skips test database sync/creation when the flag --db-push is set to false' dbPush: false, }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') }) test('Runs tests for all available sides if no side filter passed', async () => { @@ -69,7 +69,7 @@ test('Runs tests for all available sides if no side filter passed', async () => filter: ['bazinga'], }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('bazinga') expect(execa.mock.results[0].value.params).toContain('web') expect(execa.mock.results[0].value.params).toContain('api') @@ -83,7 +83,7 @@ test('Runs tests specified side if even with additional filters', async () => { expect(execa.mock.results[0].value.cmd).not.toBe('yarn rw') expect(execa.mock.results[0].value.params).not.toContain('api') - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('bazinga') expect(execa.mock.results[0].value.params).toContain('web') }) @@ -93,7 +93,7 @@ test('Does not create db when calling test with just web', async () => { filter: ['web'], }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') }) test('Passes filter param to jest command if passed', async () => { @@ -101,7 +101,7 @@ test('Passes filter param to jest command if passed', async () => { filter: ['web', 'bazinga'], }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('bazinga') }) @@ -113,7 +113,7 @@ test('Passes other flags to jest', async () => { collectCoverage: true, }) - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('-u') expect(execa.mock.results[0].value.params).toContain('--debug') expect(execa.mock.results[0].value.params).toContain('--json') @@ -127,7 +127,7 @@ test('Passes values of other flags to jest', async () => { }) // Second command because api side runs - expect(execa.mock.results[0].value.cmd).toBe('yarn jest') + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') // Note that these below tests aren't the best, since they don't check for order // But I'm making sure only 2 extra params get passed diff --git a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap index 7d930f2f34..fce1cd9b04 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap +++ b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap @@ -107,7 +107,7 @@ describe('users', () => { exports[`handler > can be called with PascalCase model name 4`] = ` { - "fileContent": "import { db } from 'src/lib/db' + "fileContent": "import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -259,7 +259,7 @@ describe('customDatums', () => { exports[`handler > can be called with PascalCase model name 8`] = ` { - "fileContent": "import { db } from 'src/lib/db' + "fileContent": "import { db } from 'src/lib/db.js' export const customDatums = () => { return db.customData.findMany() @@ -401,7 +401,7 @@ describe('users', () => { exports[`handler > can be called with camelCase model name 4`] = ` { - "fileContent": "import { db } from 'src/lib/db' + "fileContent": "import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -553,7 +553,7 @@ describe('customDatums', () => { exports[`handler > can be called with camelCase model name 8`] = ` { - "fileContent": "import { db } from 'src/lib/db' + "fileContent": "import { db } from 'src/lib/db.js' export const customDatums = () => { return db.customData.findMany() diff --git a/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap b/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap index b8e190c4f5..b202ef638b 100644 --- a/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap +++ b/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap @@ -1,7 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`in javascript mode > creates a multi word service file 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const userProfiles = () => { return db.userProfile.findMany() @@ -171,7 +171,7 @@ describe('scalarTypes', () => { `; exports[`in javascript mode > creates a single word service file 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -214,7 +214,7 @@ export const User = { `; exports[`in javascript mode > creates a single word service file with CRUD actions 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const posts = () => { return db.post.findMany() @@ -254,7 +254,7 @@ export const Post = { `; exports[`in javascript mode > creates a single word service file with a belongsTo relation 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -275,7 +275,7 @@ export const User = { `; exports[`in javascript mode > creates a single word service file with a hasMany relation 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -296,7 +296,7 @@ export const User = { `; exports[`in javascript mode > creates a single word service file with multiple relations 1`] = ` -"import { db } from 'src/lib/db' +"import { db } from 'src/lib/db.js' export const users = () => { return db.user.findMany() @@ -385,7 +385,7 @@ exports[`in typescript mode > creates a multi word service file 1`] = ` UserProfileRelationResolvers, } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const userProfiles: QueryResolvers['userProfiles'] = () => { return db.userProfile.findMany() @@ -576,7 +576,7 @@ exports[`in typescript mode > creates a single word service file 1`] = ` UserRelationResolvers, } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const users: QueryResolvers['users'] = () => { return db.user.findMany() @@ -625,7 +625,7 @@ exports[`in typescript mode > creates a single word service file with CRUD actio PostRelationResolvers, } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const posts: QueryResolvers['posts'] = () => { return db.post.findMany() @@ -667,7 +667,7 @@ export const Post: PostRelationResolvers = { exports[`in typescript mode > creates a single word service file with a belongsTo relation 1`] = ` "import type { QueryResolvers, UserRelationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const users: QueryResolvers['users'] = () => { return db.user.findMany() @@ -690,7 +690,7 @@ export const User: UserRelationResolvers = { exports[`in typescript mode > creates a single word service file with a hasMany relation 1`] = ` "import type { QueryResolvers, UserRelationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const users: QueryResolvers['users'] = () => { return db.user.findMany() @@ -713,7 +713,7 @@ export const User: UserRelationResolvers = { exports[`in typescript mode > creates a single word service file with multiple relations 1`] = ` "import type { QueryResolvers, UserRelationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const users: QueryResolvers['users'] = () => { return db.user.findMany() From 9b48e566ec54ef8472d4121de8df0032d2c9bea2 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 18 Jul 2025 16:14:54 +0200 Subject: [PATCH 046/222] fix tailwind test --- .../cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts b/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts index 858e68df53..7c85265583 100644 --- a/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts +++ b/packages/cli/src/commands/setup/ui/__tests__/tailwindcss.test.ts @@ -326,7 +326,7 @@ function setupDefaultProjectStructure( ' }', '}', ].join('\n'), - [path.join(__dirname, '../templates/postcss.config.js.template')]: '', + [path.join(__dirname, '../templates/postcss.config.cjs.template')]: '', }, APP_PATH, ) From 8142432ce8a18e7c666dd30f4530f22ca658ca0a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 11:11:13 +0200 Subject: [PATCH 047/222] testing plugins etc --- packages/storybook/src/plugins/mock-router.ts | 1 + packages/testing/package.json | 11 +++ packages/testing/src/vitest/index.ts | 3 + .../src/vitest/mockProvidersRoutesPlugin.ts | 3 +- .../vite-plugin-auto-import-mock-functions.ts | 25 ++++++ ...-plugin-cedarjs-router-import-transform.ts | 26 ++++++ ...ite-plugin-create-auth-import-transform.ts | 24 ++++++ packages/testing/src/vitest/vitest-config.ts | 2 +- packages/testing/src/web/MockProviders.tsx | 10 +-- packages/testing/src/web/MockRouter.tsx | 9 ++- .../testing/src/web/globRoutesImporter.ts | 42 ++++++++++ packages/vite/src/index.ts | 14 +++- packages/vite/src/lib/getMergedConfig.ts | 79 +++++++++---------- yarn.lock | 1 + 14 files changed, 191 insertions(+), 59 deletions(-) create mode 100644 packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts create mode 100644 packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts create mode 100644 packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts create mode 100644 packages/testing/src/web/globRoutesImporter.ts diff --git a/packages/storybook/src/plugins/mock-router.ts b/packages/storybook/src/plugins/mock-router.ts index 1043006947..da00e10ac7 100644 --- a/packages/storybook/src/plugins/mock-router.ts +++ b/packages/storybook/src/plugins/mock-router.ts @@ -8,6 +8,7 @@ export function mockRouter(): PluginOption { if (id.includes('src')) { code = code.replace( "'@cedarjs/router'", + // TODO: Use the mock router from @cedarjs/testing instead "'storybook-framework-cedarjs/dist/mocks/MockRouter'", ) } diff --git a/packages/testing/package.json b/packages/testing/package.json index 9ce3e731e4..1144313dea 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -74,6 +74,16 @@ "types": "./dist/cjs/web/index.d.ts", "default": "./dist/cjs/web/index.js" } + }, + "./web/MockRouter.js": { + "import": { + "types": "./dist/web/MockRouter.d.ts", + "default": "./dist/web/MockRouter.js" + }, + "require": { + "types": "./dist/cjs/web/MockRouter.d.ts", + "default": "./dist/cjs/web/MockRouter.js" + } } }, "files": [ @@ -117,6 +127,7 @@ "jest-watch-typeahead": "2.2.2", "msw": "1.3.4", "ts-toolbelt": "9.6.0", + "unplugin-auto-import": "19.3.0", "vitest": "3.2.4", "whatwg-fetch": "3.6.20" }, diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 6327722ccd..94c480fc40 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -1,2 +1,5 @@ export { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' +export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router-import-transform.js' +export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' +export { autoImportMockFunctionsPlugin } from './vite-plugin-auto-import-mock-functions.js' diff --git a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts index 4f1ae9b29f..38b46a5a4f 100644 --- a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts +++ b/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts @@ -17,8 +17,9 @@ export function mockProvidersRoutesPlugin(): Plugin { return null }, - load(id) { + load(id, options) { if (id === 'cedarjs:/Routes.tsx') { + console.log('cedarjs:/Routes.tsx Loading ID:', id, 'options:', options) return routes } diff --git a/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts b/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts new file mode 100644 index 0000000000..a437c577a0 --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts @@ -0,0 +1,25 @@ +import autoImport from 'unplugin-auto-import/vite' + +export function autoImportMockFunctionsPlugin() { + return autoImport({ + // targets to transform + include: [ + /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + ], + + // global imports to register + imports: [ + { + '@cedarjs/testing/web': [ + 'mockGraphQLQuery', + 'mockGraphQLMutation', + 'mockCurrentUser', + ], + }, + ], + + // We provide our mocking types elsewhere and so don't need this plugin to + // generate them. + dts: false, + }) +} diff --git a/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts b/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts new file mode 100644 index 0000000000..e87dcfc22a --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts @@ -0,0 +1,26 @@ +import type { PluginOption } from 'vite' + +/** + * Replace `@cedarjs/router` imports with imports of + * `@cedarjs/testing/web/MockRouter.js` instead + */ +export function cedarJsRouterImportTransformPlugin(): PluginOption { + return { + name: 'cedarjs-router-import-transform', + enforce: 'pre', + transform(code: string, id: string) { + console.log('cedarjs-router-import-transform id', id) + + if (id.includes('/web/src')) { + code = code.replace( + /['"]@cedarjs\/router['"]/, + "'@cedarjs/testing/web/MockRouter.js'", + ) + + console.log('cedarjs-router-import-transform code', code) + } + + return code + }, + } +} diff --git a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts new file mode 100644 index 0000000000..c10b8c6605 --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts @@ -0,0 +1,24 @@ +import type { PluginOption } from 'vite' + +export function createAuthImportTransformPlugin(): PluginOption { + return { + name: 'create-auth-import-transform', + enforce: 'pre', + transform(code: string, id: string) { + if (id.includes('web/src/auth')) { + // Remove any existing import of `createAuth` without affecting anything else. + // This regex defines 4 capture groups, where the second is `createAuth` and + // the third is an (optional) comma for subsequent named imports — we want to remove those two. + code = code.replace( + /(import\s*{\s*[^}]*)(\bcreateAuth\b)(,?)([^}]*})/, + '$1$4', + ) + // Add import to mocked `createAuth` at the top of the file. + code = + "import { createAuthentication as createAuth } from '@cedarjs/testing/auth'\n" + + code + } + return code + }, + } +} diff --git a/packages/testing/src/vitest/vitest-config.ts b/packages/testing/src/vitest/vitest-config.ts index e1bbe50765..2c43e154a1 100644 --- a/packages/testing/src/vitest/vitest-config.ts +++ b/packages/testing/src/vitest/vitest-config.ts @@ -46,7 +46,7 @@ export default defineConfig(({ mode }) => ({ find: /^@cedarjs\/router$/, replacement: path.join( NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/MockRouter.js', + '@cedarjs/testing/web/MockRouter.js', ), }, // { diff --git a/packages/testing/src/web/MockProviders.tsx b/packages/testing/src/web/MockProviders.tsx index eb8c9c2b85..b96c472fb7 100644 --- a/packages/testing/src/web/MockProviders.tsx +++ b/packages/testing/src/web/MockProviders.tsx @@ -1,18 +1,10 @@ -// NOTE: This module should not contain any nodejs functionality, because it's -// used by Storybook in the browser. - import React from 'react' -// @ts-expect-error - This is a virtual module, it doesn't have types -// The virtual module imports the user's Routes from `./web/src/Routes.{tsx,jsx}`, -// we pass the `children` from the user's Routes to `./MockRouter.Router` -// so that we can populate the `routes object` in Storybook and tests. -import UserRoutes from 'cedarjs:/Routes.tsx' - import { LocationProvider } from '@cedarjs/router' import { RedwoodProvider } from '@cedarjs/web' import { RedwoodApolloProvider } from '@cedarjs/web/apollo' +import { UserRoutes } from './globRoutesImporter.js' import { useAuth } from './mockAuth.js' import { MockParamsProvider } from './MockParamsProvider.js' diff --git a/packages/testing/src/web/MockRouter.tsx b/packages/testing/src/web/MockRouter.tsx index 0130c21be3..3e87288c3e 100644 --- a/packages/testing/src/web/MockRouter.tsx +++ b/packages/testing/src/web/MockRouter.tsx @@ -1,3 +1,8 @@ +// We use a Vite plugin to swap out imports from `@cedarjs/router` to this +// file. @see ../vitest/cedarJsRoutesImportTransformPlugin.ts +// +// It's therefore important to reexport everything that we *don't* want to mock. + import type React from 'react' import { flattenAll } from '@cedarjs/router/dist/react-util' @@ -7,14 +12,12 @@ import { flattenAll } from '@cedarjs/router/dist/react-util' import { isValidRoute } from '@cedarjs/router/dist/route-validators' import type { RouterProps } from '@cedarjs/router/dist/router' import { replaceParams } from '@cedarjs/router/dist/util' + export * from '@cedarjs/router/dist/index' export const routes: { [routeName: string]: () => string } = {} /** - * We overwrite the default `Router` export (see jest-preset). So every import - * of @cedarjs/router will import this Router instead - * * This router populates the `routes.()` utility object. */ export const Router: React.FC = ({ children }) => { diff --git a/packages/testing/src/web/globRoutesImporter.ts b/packages/testing/src/web/globRoutesImporter.ts new file mode 100644 index 0000000000..abae8af159 --- /dev/null +++ b/packages/testing/src/web/globRoutesImporter.ts @@ -0,0 +1,42 @@ +/// + +// Because this file uses the vite-specific import.meta.glob feature, we can't +// build this with esbuild. We also can't build it with vite when building the +// framework, because at that time we have no idea what user project this will +// be used in or what routes it will have. At least we can't evaluate the glob +// import at framework build time. So I copy this file as-is into the build +// output, and then tell vite to process this file when building the user's +// project. I do that by including @cedarjs/testing in `noExternal` in the +// default vite config (see lib/getMergedConfig.ts in the vite package) + +// This finds the user's Routes file in web/src/Routes.{tsx,jsx} +const defaultExports = import.meta.glob('/Routes.{tsx,jsx}', { + import: 'default', + eager: true, +}) +const routesFileName = Object.keys(defaultExports)[0] + +if (!routesFileName) { + throw new Error('@cedarjs/testing: No routes found') +} + +const routesFunction = defaultExports[routesFileName] + +if (typeof routesFunction !== 'function') { + throw new Error( + '@cedarjs/testing: Routes file does not export a React component', + ) +} + +/** + * All the routes the user has defined + * + * We render this in the `` component to populate the `routes` + * import from `@cedarjs/router` to make sure code like + * `Home` works in tests + * + * The final piece to this puzzle is to realize that the user's Routes file + * imports `@cedarjs/router`, which we replace to import from '@cedarjs/testing' + * instead using a vite plugin that we only run for vitest and storybook + */ +export const UserRoutes = routesFunction as React.FC diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 3e8652ee80..7822e24bd4 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -4,8 +4,11 @@ import type { PluginOption } from 'vite' import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' import { getConfig } from '@cedarjs/project-config' import { - mockProvidersRoutesPlugin, - mockProvidersRelativeRoutesPathsPlugin, + autoImportMockFunctionsPlugin, + // mockProvidersRoutesPlugin, + // mockProvidersRelativeRoutesPathsPlugin, + cedarJsRouterImportTransformPlugin, + createAuthImportTransformPlugin, } from '@cedarjs/testing/vitest' import { cedarCellTransform } from './plugins/vite-plugin-cedar-cell.js' @@ -59,8 +62,11 @@ export function cedar({ mode }: PluginOptions): PluginOption[] { } return [ - mode === 'test' && mockProvidersRoutesPlugin(), - mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), + // mode === 'test' && mockProvidersRoutesPlugin(), + // mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), + mode === 'test' && cedarJsRouterImportTransformPlugin(), + mode === 'test' && createAuthImportTransformPlugin(), + mode === 'test' && autoImportMockFunctionsPlugin(), cedarNodePolyfills(), cedarHtmlEnvPlugin(), cedarEntryInjectionPlugin(), diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index 2a2209790e..49a94d3900 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -2,7 +2,7 @@ import path from 'node:path' import type { InputOption } from 'rollup' import { mergeConfig } from 'vite' -import type { ConfigEnv, UserConfig } from 'vitest/config' +import type { ConfigEnv, ViteUserConfig } from 'vitest/config' import type { Config, Paths } from '@cedarjs/project-config' import { getConfig, getPaths } from '@cedarjs/project-config' @@ -17,7 +17,7 @@ import { getEnvVarDefinitions } from './envVarDefinitions.js' * build */ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { - return (userConfig: UserConfig, env: ConfigEnv): UserConfig => { + return (userConfig: ViteUserConfig, env: ConfigEnv): ViteUserConfig => { let apiHost = process.env.REDWOOD_API_HOST apiHost ??= rwConfig.api.host apiHost ??= process.env.NODE_ENV === 'production' ? '0.0.0.0' : '[::]' @@ -33,9 +33,9 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { apiPort = rwConfig.api.port } - const NODE_MODULES_PATH = path.join(rwPaths.base, 'node_modules') + // const NODE_MODULES_PATH = path.join(rwPaths.base, 'node_modules') - const defaultRwViteConfig: UserConfig = { + const defaultRwViteConfig: ViteUserConfig = { root: rwPaths.web.src, // @MARK: when we have these aliases, the warnings from the FE server go // away BUT, if you have imports like this: @@ -153,43 +153,40 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { env.mode === 'test' ? [ // Mock implementations - { - find: /^@cedarjs\/router$/, - replacement: path.join( - NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/MockRouter.js', - ), - }, - { - find: /^@cedarjs\/auth$/, - replacement: path.join( - NODE_MODULES_PATH, - '@cedarjs/auth/dist/index.js', - ), - customResolver: (id, importer) => { - // console.log('getMergedConfig auth customResolver id', id) - console.log( - 'getMergedConfig auth customResolver importer', - importer, - ) - - if (importer?.endsWith('mockAuth.js')) { - console.log('returning id', id) - return id - } - - const mockAuthId = path.join( - NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/mockAuth.js', - ) - console.log('returning id', mockAuthId) - - return path.join( - NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/mockAuth.js', - ) - }, - }, + // { + // find: /^@cedarjs\/router$/, + // replacement: path.join( + // NODE_MODULES_PATH, + // '@cedarjs/testing/dist/web/MockRouter.js', + // ), + // }, + // { + // find: /^@cedarjs\/auth$/, + // replacement: path.join( + // NODE_MODULES_PATH, + // '@cedarjs/auth/dist/index.js', + // ), + // customResolver: (id, importer) => { + // // console.log('getMergedConfig auth customResolver id', id) + // console.log( + // 'getMergedConfig auth customResolver importer', + // importer, + // ) + // if (importer?.endsWith('mockAuth.js')) { + // console.log('returning id', id) + // return id + // } + // const mockAuthId = path.join( + // NODE_MODULES_PATH, + // '@cedarjs/testing/dist/web/mockAuth.js', + // ) + // console.log('returning id', mockAuthId) + // return path.join( + // NODE_MODULES_PATH, + // '@cedarjs/testing/dist/web/mockAuth.js', + // ) + // }, + // }, ] : [], }, diff --git a/yarn.lock b/yarn.lock index c85a14a969..c5a409060d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3505,6 +3505,7 @@ __metadata: ts-toolbelt: "npm:9.6.0" tsx: "npm:4.20.3" typescript: "npm:5.6.2" + unplugin-auto-import: "npm:19.3.0" vitest: "npm:3.2.4" whatwg-fetch: "npm:3.6.20" languageName: unknown From cb3076d169e0b914f2853765725efd5226cfde49 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 11:33:03 +0200 Subject: [PATCH 048/222] fix building --- packages/testing/build.mts | 19 +++++++++++++++++-- .../testing/src/web/globRoutesImporter.ts | 16 ++++++++-------- packages/testing/tsconfig.build.json | 1 + packages/testing/tsconfig.cjs.json | 3 ++- tsconfig.base.json | 1 + 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/packages/testing/build.mts b/packages/testing/build.mts index 9252fc43a3..42e713f6f3 100644 --- a/packages/testing/build.mts +++ b/packages/testing/build.mts @@ -1,6 +1,11 @@ import fs from 'node:fs' -import { buildCjs, buildEsm } from '@cedarjs/framework-tools' +import { + build, + defaultBuildOptions, + defaultIgnorePatterns, + buildEsm, +} from '@cedarjs/framework-tools' import { generateTypesCjs, generateTypesEsm, @@ -10,7 +15,17 @@ import { await buildEsm() await generateTypesEsm() -await buildCjs() +// CJS Build +await build({ + entryPointOptions: { + ignore: [...defaultIgnorePatterns, '**/globRoutesImporter.ts'], + }, + buildOptions: { + ...defaultBuildOptions, + tsconfig: 'tsconfig.cjs.json', + outdir: 'dist/cjs', + }, +}) await generateTypesCjs() await insertCommonJsPackageJson({ buildFileUrl: import.meta.url, diff --git a/packages/testing/src/web/globRoutesImporter.ts b/packages/testing/src/web/globRoutesImporter.ts index abae8af159..e13aeb15fe 100644 --- a/packages/testing/src/web/globRoutesImporter.ts +++ b/packages/testing/src/web/globRoutesImporter.ts @@ -1,13 +1,13 @@ /// -// Because this file uses the vite-specific import.meta.glob feature, we can't -// build this with esbuild. We also can't build it with vite when building the -// framework, because at that time we have no idea what user project this will -// be used in or what routes it will have. At least we can't evaluate the glob -// import at framework build time. So I copy this file as-is into the build -// output, and then tell vite to process this file when building the user's -// project. I do that by including @cedarjs/testing in `noExternal` in the -// default vite config (see lib/getMergedConfig.ts in the vite package) +// We're building this file with esbuild, which doesn't understand the vite- +// specific `import.meta.glob` feature. So it'll just leave it as is, which is +// actually exactly what we want. We can't evaluate the glob import when +// building the framework, because at that time we have no idea what user +// project this will be used in or what routes it will have. So instead I tell +// vite to process this file when building the user's project. I do that by +// including @cedarjs/testing in `noExternal` in the default vite config (see +// lib/getMergedConfig.ts in the vite package) // This finds the user's Routes file in web/src/Routes.{tsx,jsx} const defaultExports = import.meta.glob('/Routes.{tsx,jsx}', { diff --git a/packages/testing/tsconfig.build.json b/packages/testing/tsconfig.build.json index ea41ce9c76..26eb2e179a 100644 --- a/packages/testing/tsconfig.build.json +++ b/packages/testing/tsconfig.build.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "extends": "../../tsconfig.base.json", "compilerOptions": { "rootDir": "src", diff --git a/packages/testing/tsconfig.cjs.json b/packages/testing/tsconfig.cjs.json index 4a9ea99b92..c4d3e28fd4 100644 --- a/packages/testing/tsconfig.cjs.json +++ b/packages/testing/tsconfig.cjs.json @@ -6,7 +6,8 @@ }, "exclude": [ "**/__tests__", - // The vitest files are only used by ESM projects + // The following files are only used by ESM projects + "globRoutesImporter.ts", "src/vitest" ] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 8b450e1f2c..8ab5d0232b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,4 +1,5 @@ { + "$schema": "https://json.schemastore.org/tsconfig", "compilerOptions": { "target": "esnext", "moduleResolution": "node", From 6a0b7eab41e2252aaf9940847448b149d5ad1c2f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 11:57:53 +0200 Subject: [PATCH 049/222] Skip building cjs types for now --- packages/testing/build.mts | 2 +- packages/testing/package.json | 2 +- packages/testing/tsconfig.cjs.json | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/packages/testing/build.mts b/packages/testing/build.mts index 42e713f6f3..2bdaf28dc5 100644 --- a/packages/testing/build.mts +++ b/packages/testing/build.mts @@ -26,7 +26,7 @@ await build({ outdir: 'dist/cjs', }, }) -await generateTypesCjs() +// await generateTypesCjs() await insertCommonJsPackageJson({ buildFileUrl: import.meta.url, }) diff --git a/packages/testing/package.json b/packages/testing/package.json index 1144313dea..df4ce1fd9b 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -97,7 +97,7 @@ "build": "tsx ./build.mts && yarn build:types", "build:pack": "yarn pack -o cedarjs-testing.tgz", "build:types": "tsc --build --verbose ./tsconfig.build.json", - "build:types-cjs": "tsc --build --verbose tsconfig.cjs.json", + "build:types-cjs": "tsc --build --verbose ./tsconfig.cjs.json", "build:watch": "nodemon --watch src --ext 'js,jsx,ts,tsx' --ignore dist --exec 'yarn build'", "check:attw": "yarn rw-fwtools-attw", "check:package": "concurrently npm:check:attw yarn:publint", diff --git a/packages/testing/tsconfig.cjs.json b/packages/testing/tsconfig.cjs.json index c4d3e28fd4..50b6a77e39 100644 --- a/packages/testing/tsconfig.cjs.json +++ b/packages/testing/tsconfig.cjs.json @@ -6,8 +6,9 @@ }, "exclude": [ "**/__tests__", - // The following files are only used by ESM projects - "globRoutesImporter.ts", + "", + "// The following files are only used by ESM projects", + "src/web/globRoutesImporter.ts", "src/vitest" ] } From eabf42d6e004a7dd7d8b9b8318fb50acd2a4754e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 12:15:49 +0200 Subject: [PATCH 050/222] esm/cjs comment --- packages/testing/build.mts | 3 ++- .../src/vitest/vite-plugin-cedarjs-router-import-transform.ts | 4 ---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/testing/build.mts b/packages/testing/build.mts index 2bdaf28dc5..97c048c3e2 100644 --- a/packages/testing/build.mts +++ b/packages/testing/build.mts @@ -7,7 +7,6 @@ import { buildEsm, } from '@cedarjs/framework-tools' import { - generateTypesCjs, generateTypesEsm, insertCommonJsPackageJson, } from '@cedarjs/framework-tools/generateTypes' @@ -26,6 +25,8 @@ await build({ outdir: 'dist/cjs', }, }) +// Skipping this for now. Need to decide if I should support both ESM and CJS +// projects once ESM support is done or not // await generateTypesCjs() await insertCommonJsPackageJson({ buildFileUrl: import.meta.url, diff --git a/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts b/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts index e87dcfc22a..027f02a37a 100644 --- a/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts +++ b/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts @@ -9,15 +9,11 @@ export function cedarJsRouterImportTransformPlugin(): PluginOption { name: 'cedarjs-router-import-transform', enforce: 'pre', transform(code: string, id: string) { - console.log('cedarjs-router-import-transform id', id) - if (id.includes('/web/src')) { code = code.replace( /['"]@cedarjs\/router['"]/, "'@cedarjs/testing/web/MockRouter.js'", ) - - console.log('cedarjs-router-import-transform code', code) } return code From f070c491bfa9c47763be3c42ea7cd448c329a0c4 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 12:28:26 +0200 Subject: [PATCH 051/222] improve regex --- .../src/vitest/vite-plugin-create-auth-import-transform.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts index c10b8c6605..3f81c94bb3 100644 --- a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts +++ b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts @@ -9,8 +9,10 @@ export function createAuthImportTransformPlugin(): PluginOption { // Remove any existing import of `createAuth` without affecting anything else. // This regex defines 4 capture groups, where the second is `createAuth` and // the third is an (optional) comma for subsequent named imports — we want to remove those two. + // Using a negative lookbehind as recommended by + // https://codeql.github.com/codeql-query-help/javascript/js-polynomial-redos/ code = code.replace( - /(import\s*{\s*[^}]*)(\bcreateAuth\b)(,?)([^}]*})/, + /(import\s*{(? Date: Sat, 19 Jul 2025 13:45:48 +0200 Subject: [PATCH 052/222] include cjs config files in eslint config --- packages/eslint-config/shared.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/eslint-config/shared.js b/packages/eslint-config/shared.js index e27ca34cf5..506a85bc97 100644 --- a/packages/eslint-config/shared.js +++ b/packages/eslint-config/shared.js @@ -182,7 +182,9 @@ module.exports = { 'babel.config.js', '.eslintrc.js', '*.config.js', + '*.config.cjs', 'jest.setup.js', + 'jest.setup.cjs', ], env: { node: true, From bcdde709520e5b5ecf40c0080bcee07d1e4ee569 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 14:05:50 +0200 Subject: [PATCH 053/222] update test project fixture --- .../api/src/services/contacts/contacts.ts | 2 +- .../api/src/services/posts/posts.ts | 2 +- .../api/src/services/users/users.ts | 2 +- __fixtures__/test-project/package.json | 2 +- __fixtures__/test-project/prettier.config.cjs | 2 +- .../{postcss.config.js => postcss.config.cjs} | 2 +- ...tailwind.config.js => tailwind.config.cjs} | 2 +- .../test-project/web/src/scaffold.css | 315 +++++++++++++----- 8 files changed, 242 insertions(+), 87 deletions(-) rename __fixtures__/test-project/web/config/{postcss.config.js => postcss.config.cjs} (94%) rename __fixtures__/test-project/web/config/{tailwind.config.js => tailwind.config.cjs} (91%) diff --git a/__fixtures__/test-project/api/src/services/contacts/contacts.ts b/__fixtures__/test-project/api/src/services/contacts/contacts.ts index 453651e37b..b041558483 100644 --- a/__fixtures__/test-project/api/src/services/contacts/contacts.ts +++ b/__fixtures__/test-project/api/src/services/contacts/contacts.ts @@ -1,6 +1,6 @@ import type { QueryResolvers, MutationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const contacts: QueryResolvers['contacts'] = () => { return db.contact.findMany() diff --git a/__fixtures__/test-project/api/src/services/posts/posts.ts b/__fixtures__/test-project/api/src/services/posts/posts.ts index eaeff31fb7..13df31ed97 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.ts @@ -4,7 +4,7 @@ import type { PostRelationResolvers, } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const posts: QueryResolvers['posts'] = () => { return db.post.findMany() diff --git a/__fixtures__/test-project/api/src/services/users/users.ts b/__fixtures__/test-project/api/src/services/users/users.ts index 1160d12f84..380ef92c64 100644 --- a/__fixtures__/test-project/api/src/services/users/users.ts +++ b/__fixtures__/test-project/api/src/services/users/users.ts @@ -1,6 +1,6 @@ import type { QueryResolvers, UserRelationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export {} diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index 27aaf9bc00..1295a98810 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -28,4 +28,4 @@ "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", "react-is": "19.0.0-rc-f2df5694-20240916" } -} +} \ No newline at end of file diff --git a/__fixtures__/test-project/prettier.config.cjs b/__fixtures__/test-project/prettier.config.cjs index 3ed0f1e84d..9d81988854 100644 --- a/__fixtures__/test-project/prettier.config.cjs +++ b/__fixtures__/test-project/prettier.config.cjs @@ -15,6 +15,6 @@ module.exports = { }, }, ], - tailwindConfig: './web/config/tailwind.config.js', + tailwindConfig: './web/config/tailwind.config.cjs', plugins: ['prettier-plugin-tailwindcss'], } diff --git a/__fixtures__/test-project/web/config/postcss.config.js b/__fixtures__/test-project/web/config/postcss.config.cjs similarity index 94% rename from __fixtures__/test-project/web/config/postcss.config.js rename to __fixtures__/test-project/web/config/postcss.config.cjs index 3cdce19941..f6d96e144e 100644 --- a/__fixtures__/test-project/web/config/postcss.config.js +++ b/__fixtures__/test-project/web/config/postcss.config.cjs @@ -3,7 +3,7 @@ const path = require('path') module.exports = { plugins: [ require('tailwindcss/nesting'), - require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')), + require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.cjs')), require('autoprefixer'), ], } diff --git a/__fixtures__/test-project/web/config/tailwind.config.js b/__fixtures__/test-project/web/config/tailwind.config.cjs similarity index 91% rename from __fixtures__/test-project/web/config/tailwind.config.js rename to __fixtures__/test-project/web/config/tailwind.config.cjs index 613a94fcab..383b9a16be 100644 --- a/__fixtures__/test-project/web/config/tailwind.config.js +++ b/__fixtures__/test-project/web/config/tailwind.config.cjs @@ -1,7 +1,7 @@ const { join } = require('node:path') /** @type {import('tailwindcss').Config} */ -module.exports = { +export default { content: [join(__dirname, '../src/**/*.{js,jsx,ts,tsx}')], theme: { extend: {}, diff --git a/__fixtures__/test-project/web/src/scaffold.css b/__fixtures__/test-project/web/src/scaffold.css index 736897ab98..cfa7abf033 100644 --- a/__fixtures__/test-project/web/src/scaffold.css +++ b/__fixtures__/test-project/web/src/scaffold.css @@ -1,243 +1,398 @@ -.rw-scaffold { - @apply bg-white text-gray-600; +/* + normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css +*/ + +.rw-scaffold *, +.rw-scaffold ::after, +.rw-scaffold ::before { + box-sizing: inherit; +} +.rw-scaffold main { + color: #4a5568; + display: block; } .rw-scaffold h1, .rw-scaffold h2 { - @apply m-0; + margin: 0; } .rw-scaffold a { - @apply bg-transparent; + background-color: transparent; } .rw-scaffold ul { - @apply m-0 p-0; + margin: 0; + padding: 0; +} +.rw-scaffold input { + font-family: inherit; + font-size: 100%; + overflow: visible; } .rw-scaffold input:-ms-input-placeholder { - @apply text-gray-500; + color: #a0aec0; } .rw-scaffold input::-ms-input-placeholder { - @apply text-gray-500; + color: #a0aec0; } .rw-scaffold input::placeholder { - @apply text-gray-500; + color: #a0aec0; +} +.rw-scaffold table { + border-collapse: collapse; +} + +/* + Style +*/ + +.rw-scaffold, +.rw-toast { + background-color: #fff; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", + Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; } .rw-header { - @apply flex justify-between py-4 px-8; + display: flex; + justify-content: space-between; + padding: 1rem 2rem 1rem 2rem; } .rw-main { - @apply mx-4 pb-4; + margin-left: 1rem; + margin-right: 1rem; + padding-bottom: 1rem; } .rw-segment { - @apply rounded-lg overflow-hidden w-full border border-gray-200; - scrollbar-color: theme("colors.zinc.400") transparent; + border-radius: 0.5rem; + border-width: 1px; + border-color: #e5e7eb; + overflow: hidden; + width: 100%; + scrollbar-color: #a1a1aa transparent; } .rw-segment::-webkit-scrollbar { height: initial; } .rw-segment::-webkit-scrollbar-track { - @apply border-gray-200 bg-transparent border-solid rounded-t-none rounded-b-[10px] border-0 border-t p-[2px]; + background-color: transparent; + border-color: #e2e8f0; + border-style: solid; + border-radius: 0 0 10px 10px; + border-width: 1px 0 0 0; + padding: 2px; } .rw-segment::-webkit-scrollbar-thumb { - @apply bg-zinc-400 bg-clip-content border-[3px] border-solid border-transparent rounded-full; + background-color: #a1a1aa; + background-clip: content-box; + border: 3px solid transparent; + border-radius: 10px; } .rw-segment-header { - @apply bg-gray-200 text-gray-700 py-3 px-4; + background-color: #e2e8f0; + color: #4a5568; + padding: 0.75rem 1rem; } .rw-segment-main { - @apply bg-gray-100 p-4; + background-color: #f7fafc; + padding: 1rem; } .rw-link { - @apply text-blue-400 underline; + color: #4299e1; + text-decoration: underline; } .rw-link:hover { - @apply text-blue-500; + color: #2b6cb0; } .rw-forgot-link { - @apply text-xs text-gray-400 text-right mt-1 underline; + font-size: 0.75rem; + color: #a0aec0; + text-align: right; + margin-top: 0.1rem; } .rw-forgot-link:hover { - @apply text-blue-500; + font-size: 0.75rem; + color: #4299e1; } .rw-heading { - @apply font-semibold; + font-weight: 600; } .rw-heading.rw-heading-primary { - @apply text-xl; + font-size: 1.25rem; } .rw-heading.rw-heading-secondary { - @apply text-sm; + font-size: 0.875rem; } .rw-heading .rw-link { - @apply text-gray-600 no-underline; + color: #4a5568; + text-decoration: none; } .rw-heading .rw-link:hover { - @apply text-gray-900 underline; + color: #1a202c; + text-decoration: underline; } .rw-cell-error { - @apply text-sm font-semibold; + font-size: 90%; + font-weight: 600; } .rw-form-wrapper { - @apply text-sm -mt-4; + box-sizing: border-box; + font-size: 0.875rem; + margin-top: -1rem; } .rw-cell-error, .rw-form-error-wrapper { - @apply p-4 bg-red-50 text-red-600 border border-red-100 rounded my-4; + padding: 1rem; + background-color: #fff5f5; + color: #c53030; + border-width: 1px; + border-color: #feb2b2; + border-radius: 0.25rem; + margin: 1rem 0; } .rw-form-error-title { - @apply m-0 font-semibold; + margin-top: 0; + margin-bottom: 0; + font-weight: 600; } .rw-form-error-list { - @apply mt-2 list-disc list-inside; + margin-top: 0.5rem; + list-style-type: disc; + list-style-position: inside; } .rw-button { - @apply flex justify-center py-1 px-4 border-0 rounded bg-gray-200 text-gray-500 text-xs font-semibold uppercase tracking-wide leading-loose no-underline cursor-pointer transition duration-100; + border: none; + color: #718096; + cursor: pointer; + display: flex; + justify-content: center; + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 1rem; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 0.025em; + border-radius: 0.25rem; + line-height: 2; + border: 0; } .rw-button:hover { - @apply bg-gray-500 text-white; + background-color: #718096; + color: #fff; } .rw-button.rw-button-small { - @apply text-xs rounded-sm py-1 px-2; + font-size: 0.75rem; + border-radius: 0.125rem; + padding: 0.25rem 0.5rem; + line-height: inherit; } .rw-button.rw-button-green { - @apply bg-green-500 text-white; + background-color: #48bb78; + color: #fff; } .rw-button.rw-button-green:hover { - @apply bg-green-700; + background-color: #38a169; + color: #fff; } .rw-button.rw-button-blue { - @apply bg-blue-500 text-white; + background-color: #3182ce; + color: #fff; } .rw-button.rw-button-blue:hover { - @apply bg-blue-700; + background-color: #2b6cb0; } .rw-button.rw-button-red { - @apply bg-red-500 text-white; + background-color: #e53e3e; + color: #fff; } .rw-button.rw-button-red:hover { - @apply bg-red-700 text-white; + background-color: #c53030; } .rw-button-icon { - @apply text-xl leading-5 mr-1; + font-size: 1.25rem; + line-height: 1; + margin-right: 0.25rem; } .rw-button-group { - @apply flex justify-center my-3 mx-2; + display: flex; + justify-content: center; + margin: 0.75rem 0.5rem; } .rw-button-group .rw-button { - @apply mx-1; + margin: 0 0.25rem; } .rw-form-wrapper .rw-button-group { - @apply mt-8; + margin-top: 2rem; + margin-bottom: 0; } .rw-label { - @apply block mt-6 text-gray-600 font-semibold text-left; + display: block; + margin-top: 1.5rem; + color: #4a5568; + font-weight: 600; + text-align: left; } .rw-label.rw-label-error { - @apply text-red-600; + color: #c53030; } .rw-input { - @apply block mt-2 w-full p-2 bg-white border border-gray-200 rounded outline-none; + display: block; + margin-top: 0.5rem; + width: 100%; + padding: 0.5rem; + border-width: 1px; + border-style: solid; + border-color: #e2e8f0; + color: #4a5568; + border-radius: 0.25rem; + outline: none; +} +.rw-check-radio-item-none { + color: #4a5568; } .rw-check-radio-items { - @apply flex justify-items-center; + display: flex; + justify-items: center; } -.rw-check-radio-item-none { - @apply text-gray-600; +.rw-input[type="checkbox"] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; } -.rw-input[type="checkbox"], .rw-input[type="radio"] { - @apply inline w-4 ml-0 mr-1 mt-1; + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; } .rw-input:focus { - @apply border-gray-400; + border-color: #a0aec0; } .rw-input-error { - @apply border-red-600 text-red-600; + border-color: #c53030; + color: #c53030; } + .rw-input-error:focus { - @apply border-red-600 outline-none; + outline: none; + border-color: #c53030; box-shadow: 0 0 5px #c53030; } + .rw-field-error { - @apply block mt-1 font-semibold text-xs text-red-600 uppercase; + display: block; + margin-top: 0.25rem; + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; + color: #c53030; } .rw-table-wrapper-responsive { - @apply overflow-x-auto; + overflow-x: auto; } .rw-table-wrapper-responsive .rw-table { min-width: 48rem; } .rw-table { - @apply w-full text-sm; + table-layout: auto; + width: 100%; + font-size: 0.875rem; } .rw-table th, .rw-table td { - @apply p-3; + padding: 0.75rem; } .rw-table td { - @apply bg-white text-gray-900; + background-color: #ffffff; + color: #1a202c; } .rw-table tr:nth-child(odd) td, .rw-table tr:nth-child(odd) th { - @apply bg-gray-50; + background-color: #f7fafc; } .rw-table thead tr { - @apply bg-gray-200 text-gray-600; + color: #4a5568; } .rw-table th { - @apply font-semibold text-left; + font-weight: 600; + text-align: left; } .rw-table thead th { - @apply text-left; + background-color: #e2e8f0; + text-align: left; } .rw-table tbody th { - @apply text-right; + text-align: right; } @media (min-width: 768px) { .rw-table tbody th { - @apply w-1/5; + width: 20%; } } .rw-table tbody tr { - @apply border-t border-gray-200; + border-top-width: 1px; } .rw-table input { - @apply ml-0; + margin-left: 0; } .rw-table-actions { - @apply flex justify-end items-center h-4 pr-1; + display: flex; + justify-content: flex-end; + align-items: center; + height: 17px; + padding-right: 0.25rem; } .rw-table-actions .rw-button { - @apply bg-transparent; + background-color: transparent; } .rw-table-actions .rw-button:hover { - @apply bg-gray-500 text-white; + background-color: #718096; + color: #fff; } .rw-table-actions .rw-button-blue { - @apply text-blue-500; + color: #3182ce; } .rw-table-actions .rw-button-blue:hover { - @apply bg-blue-500 text-white; + background-color: #3182ce; + color: #fff; } .rw-table-actions .rw-button-red { - @apply text-red-600; + color: #e53e3e; } .rw-table-actions .rw-button-red:hover { - @apply bg-red-600 text-white; + background-color: #e53e3e; + color: #fff; } .rw-text-center { - @apply text-center; + text-align: center; } .rw-login-container { - @apply flex items-center justify-center flex-wrap mx-auto w-96 my-16; + display: flex; + align-items: center; + justify-content: center; + width: 24rem; + margin: 4rem auto; + flex-wrap: wrap; } .rw-login-container .rw-form-wrapper { - @apply w-full text-center; + width: 100%; + text-align: center; } .rw-login-link { - @apply mt-4 text-gray-600 text-sm text-center w-full; + margin-top: 1rem; + color: #4a5568; + font-size: 90%; + text-align: center; + flex-basis: 100%; } .rw-webauthn-wrapper { - @apply mt-6 mx-4 leading-6; + margin: 1.5rem 1rem 1rem; + line-height: 1.4; } .rw-webauthn-wrapper h2 { - @apply mb-4 text-xl font-bold; + font-size: 150%; + font-weight: bold; + margin-bottom: 1rem; } From 69347af3f12eb0970ed3f1d2fc0ecae9ece716c9 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 14:06:19 +0200 Subject: [PATCH 054/222] vite-plugin-creaet-auth-import-transform: simplify regex --- .../src/vitest/vite-plugin-create-auth-import-transform.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts index 3f81c94bb3..b867311da4 100644 --- a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts +++ b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts @@ -9,10 +9,8 @@ export function createAuthImportTransformPlugin(): PluginOption { // Remove any existing import of `createAuth` without affecting anything else. // This regex defines 4 capture groups, where the second is `createAuth` and // the third is an (optional) comma for subsequent named imports — we want to remove those two. - // Using a negative lookbehind as recommended by - // https://codeql.github.com/codeql-query-help/javascript/js-polynomial-redos/ code = code.replace( - /(import\s*{(? Date: Sat, 19 Jul 2025 14:14:52 +0200 Subject: [PATCH 055/222] anchor regex --- .../src/vitest/vite-plugin-create-auth-import-transform.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts index b867311da4..76c23f869a 100644 --- a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts +++ b/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts @@ -10,7 +10,7 @@ export function createAuthImportTransformPlugin(): PluginOption { // This regex defines 4 capture groups, where the second is `createAuth` and // the third is an (optional) comma for subsequent named imports — we want to remove those two. code = code.replace( - /(import\s*{[^}]*)(\bcreateAuth\b)(,?)([^}]*})/, + /(^\s*import\s*{[^}]*?)(\bcreateAuth\b)(,?)([^}]*})/, '$1$4', ) // Add import to mocked `createAuth` at the top of the file. From 4d67b3578899a64f13929f99791abb4b8cd368e3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 15:23:25 +0200 Subject: [PATCH 056/222] Remove check for jest config --- packages/cli/src/commands/testHandler.js | 33 ------------------------ 1 file changed, 33 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index ec13a99b3a..c39f647962 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -1,13 +1,9 @@ -import path from 'path' - import execa from 'execa' -import fs from 'fs-extra' import { recordTelemetryAttributes } from '@cedarjs/cli-helpers' import { ensurePosixPath } from '@cedarjs/project-config' import { errorTelemetry, timedTelemetry } from '@cedarjs/telemetry' -import c from '../lib/colors.js' import { getPaths } from '../lib/index.js' import * as project from '../lib/project.js' @@ -30,32 +26,6 @@ function isInMercurialRepository() { } } -function isJestConfigFile(sides) { - for (let side of sides) { - try { - if (sides.includes(side)) { - const jestConfigExists = - fs.existsSync(path.join(side, 'jest.config.js')) || - fs.existsSync(path.join(side, 'jest.config.cjs')) || - fs.existsSync(path.join(side, 'jest.config.ts')) - - if (!jestConfigExists) { - console.error( - c.error( - `\nError: Missing Jest config file ${side}/jest.config.cjs` + - '\nTo add this file, run `npx @cedarjs/codemods update-jest-config`\n', - ), - ) - throw new Error(`Error: Jest config file not found in ${side} side`) - } - } - } catch (e) { - errorTelemetry(process.argv, e.message) - process.exit(e?.exitCode || 1) - } - } -} - export const handler = async ({ filter: filterParams = [], watch = true, @@ -133,9 +103,6 @@ export const handler = async ({ jestArgs.push('--projects', ...sides) } - //checking if Jest config files exists in each of the sides - isJestConfigFile(sides) - try { const cacheDirDb = `file:${ensurePosixPath( rwjsPaths.generated.base, From 882667e2f23419571d7954fb03474ce77b1ef14a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 16:05:44 +0200 Subject: [PATCH 057/222] fix(routes): Don't import Routes from sub-folders --- packages/babel-config/src/web.ts | 2 +- .../rollupPlugins/rollup-plugin-cedarjs-routes-auto-loader.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/babel-config/src/web.ts b/packages/babel-config/src/web.ts index c4f850cbd3..943c045190 100644 --- a/packages/babel-config/src/web.ts +++ b/packages/babel-config/src/web.ts @@ -125,7 +125,7 @@ export const getWebSideOverrides = ( // Automatically import files in `./web/src/pages/*` in to // the `./web/src/Routes.[ts|jsx]` file. { - test: /Routes.(js|tsx|jsx)$/, + test: /src\/Routes.(js|tsx|jsx)$/, plugins: [ [ require('./plugins/babel-plugin-redwood-routes-auto-loader').default, diff --git a/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-routes-auto-loader.ts b/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-routes-auto-loader.ts index 82c6cb0ec5..bb263d1bab 100644 --- a/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-routes-auto-loader.ts +++ b/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-routes-auto-loader.ts @@ -81,7 +81,7 @@ export function cedarjsRoutesAutoLoaderPlugin({ ) } - const filter = createFilter(['**/Routes.{tsx,jsx,js,ts}']) + const filter = createFilter(['**/src/Routes.{tsx,jsx,js,ts}']) return { name: 'cedarjs-routes-auto-loader', From cad471b531b1e569ba90611cd2967be4f1ec9a11 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 16:19:04 +0200 Subject: [PATCH 058/222] improve testing --- packages/cli/src/commands/testHandler.js | 11 ++++++----- packages/testing/src/web/globRoutesImporter.ts | 16 +++++++++++----- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index c39f647962..4b9175d0b5 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -80,7 +80,7 @@ export const handler = async ({ ), ] - const jestArgs = [ + const vitestArgs = [ ...jestFilterArgs, ...forwardJestFlags, collectCoverage ? '--collectCoverage' : null, @@ -91,7 +91,7 @@ export const handler = async ({ // because of https://github.com/facebook/create-react-app/issues/5210 if (watch && !process.env.CI && !collectCoverage) { const hasSourceControl = isInGitRepository() || isInMercurialRepository() - jestArgs.push(hasSourceControl ? '--watch' : '--watchAll') + vitestArgs.push(hasSourceControl ? '--watch' : '--watchAll') } // if no sides declared with yargs, default to all sides @@ -100,7 +100,7 @@ export const handler = async ({ } if (sides.length > 0) { - jestArgs.push('--projects', ...sides) + vitestArgs.push('--project', ...sides) } try { @@ -115,13 +115,14 @@ export const handler = async ({ process.env.SKIP_DB_PUSH = '1' } - console.log('jestArgs', jestArgs) + console.log('Vitest arguments:', JSON.stringify(vitestArgs, null, 2)) // **NOTE** There is no official way to run Jest programmatically, // so we're running it via execa, since `jest.run()` is a bit unstable. // https://github.com/facebook/jest/issues/5048 + // TODO: Run vitest programmatically. See https://vitest.dev/advanced/api/ const runCommand = async () => { - await execa('yarn vitest', ['run'], { + await execa('yarn vitest', vitestArgs, { cwd: rwjsPaths.base, shell: true, stdio: 'inherit', diff --git a/packages/testing/src/web/globRoutesImporter.ts b/packages/testing/src/web/globRoutesImporter.ts index e13aeb15fe..13948c9a61 100644 --- a/packages/testing/src/web/globRoutesImporter.ts +++ b/packages/testing/src/web/globRoutesImporter.ts @@ -9,11 +9,17 @@ // including @cedarjs/testing in `noExternal` in the default vite config (see // lib/getMergedConfig.ts in the vite package) -// This finds the user's Routes file in web/src/Routes.{tsx,jsx} -const defaultExports = import.meta.glob('/Routes.{tsx,jsx}', { - import: 'default', - eager: true, -}) +// We want to find the user's Routes file in web/src/Routes.{tsx,jsx} +// When running tests from the root of the user's project, vite will see the +// path as `/src/Routes.tsx`. When running the tests from the web/ directory, +// vite will see the path as `/Routes.tsx` +const defaultExports = import.meta.glob( + ['/src/Routes.{tsx,jsx}', '/Routes.{tsx,jsx}'], + { + import: 'default', + eager: true, + }, +) const routesFileName = Object.keys(defaultExports)[0] if (!routesFileName) { From 4bf055f291fa6e63677534fa644b6d5a5704b6cc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 16:26:15 +0200 Subject: [PATCH 059/222] fix test test --- packages/cli/src/commands/__tests__/test.test.js | 2 +- packages/cli/src/commands/testHandler.js | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/commands/__tests__/test.test.js b/packages/cli/src/commands/__tests__/test.test.js index bc19a4693d..63110b0828 100644 --- a/packages/cli/src/commands/__tests__/test.test.js +++ b/packages/cli/src/commands/__tests__/test.test.js @@ -52,7 +52,7 @@ test('Syncs or creates test database when the flag --db-push is set to true', as expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') - expect(execa.mock.results[0].value.params).toContain('--projects', 'api') + expect(execa.mock.results[0].value.params).toContain('--project', 'api') }) test('Skips test database sync/creation when the flag --db-push is set to false', async () => { diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 4b9175d0b5..2926ce767d 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -115,8 +115,6 @@ export const handler = async ({ process.env.SKIP_DB_PUSH = '1' } - console.log('Vitest arguments:', JSON.stringify(vitestArgs, null, 2)) - // **NOTE** There is no official way to run Jest programmatically, // so we're running it via execa, since `jest.run()` is a bit unstable. // https://github.com/facebook/jest/issues/5048 From aac576e638b94b5afc4c087148732deda525bbb5 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 19 Jul 2025 16:28:20 +0200 Subject: [PATCH 060/222] fix project config test --- packages/project-config/src/__tests__/paths.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/project-config/src/__tests__/paths.test.ts b/packages/project-config/src/__tests__/paths.test.ts index acd4158304..99ac9ea5f4 100644 --- a/packages/project-config/src/__tests__/paths.test.ts +++ b/packages/project-config/src/__tests__/paths.test.ts @@ -68,7 +68,7 @@ const DEFAULT_PATHS = { html: ['web', 'src', 'index.html'], config: ['web', 'config'], viteConfig: ['web', 'vite.config.ts'], - postcss: ['web', 'config', 'postcss.config.js'], + postcss: ['web', 'config', 'postcss.config.cjs'], storybookConfig: ['web', '.storybook', 'main.js'], storybookPreviewConfig: ['web', '.storybook', 'preview.js'], storybookManagerConfig: ['web', '.storybook', 'manager.js'], From 8a7956800eb970e273606e942526200cd4f6c1cc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 21 Jul 2025 06:27:44 +0200 Subject: [PATCH 061/222] match vitest watch behavior with jest --- packages/cli/src/commands/testHandler.js | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 2926ce767d..eda326227b 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -7,25 +7,6 @@ import { errorTelemetry, timedTelemetry } from '@cedarjs/telemetry' import { getPaths } from '../lib/index.js' import * as project from '../lib/project.js' -// https://github.com/facebook/create-react-app/blob/cbad256a4aacfc3084be7ccf91aad87899c63564/packages/react-scripts/scripts/test.js#L39 -function isInGitRepository() { - try { - execa.commandSync('git rev-parse --is-inside-work-tree') - return true - } catch { - return false - } -} - -function isInMercurialRepository() { - try { - execa.commandSync('hg --cwd . root') - return true - } catch { - return false - } -} - export const handler = async ({ filter: filterParams = [], watch = true, @@ -90,8 +71,7 @@ export const handler = async ({ // If the user wants to watch, set the proper watch flag based on what kind of repo this is // because of https://github.com/facebook/create-react-app/issues/5210 if (watch && !process.env.CI && !collectCoverage) { - const hasSourceControl = isInGitRepository() || isInMercurialRepository() - vitestArgs.push(hasSourceControl ? '--watch' : '--watchAll') + vitestArgs.push('--watch') } // if no sides declared with yargs, default to all sides @@ -120,7 +100,7 @@ export const handler = async ({ // https://github.com/facebook/jest/issues/5048 // TODO: Run vitest programmatically. See https://vitest.dev/advanced/api/ const runCommand = async () => { - await execa('yarn vitest', vitestArgs, { + await execa('yarn vitest run', vitestArgs, { cwd: rwjsPaths.base, shell: true, stdio: 'inherit', From adff62521196ca08e29b9f0f0c8b64b45845a492 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 21 Jul 2025 06:35:41 +0200 Subject: [PATCH 062/222] vitest: Fix running all sides by default --- packages/cli/src/commands/testHandler.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index eda326227b..626f6f97f4 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -79,9 +79,7 @@ export const handler = async ({ project.sides().forEach((side) => sides.push(side)) } - if (sides.length > 0) { - vitestArgs.push('--project', ...sides) - } + sides.forEach((side) => vitestArgs.push('--project', side)) try { const cacheDirDb = `file:${ensurePosixPath( From 4c466dbfd2d8ee24508de6290c0ee2ee44a41e47 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 21 Jul 2025 10:28:30 +0200 Subject: [PATCH 063/222] Remove unused vitest-config.ts file --- packages/testing/src/vitest/vitest-config.ts | 83 -------------------- 1 file changed, 83 deletions(-) delete mode 100644 packages/testing/src/vitest/vitest-config.ts diff --git a/packages/testing/src/vitest/vitest-config.ts b/packages/testing/src/vitest/vitest-config.ts deleted file mode 100644 index 2c43e154a1..0000000000 --- a/packages/testing/src/vitest/vitest-config.ts +++ /dev/null @@ -1,83 +0,0 @@ -import dns from 'node:dns' -import path from 'node:path' - -import { defineConfig } from 'vitest/config' - -import { getPaths } from '@cedarjs/project-config' - -import { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' -import { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' - -const rwjsPaths = getPaths() -const NODE_MODULES_PATH = path.join(rwjsPaths.base, 'node_modules') - -// So that Vite will load on localhost instead of `127.0.0.1`. -// See: https://vitejs.dev/config/server-options.html#server-host. -dns.setDefaultResultOrder('verbatim') - -export default defineConfig(({ mode }) => ({ - plugins: [ - mode === 'test' && mockProvidersRoutesPlugin(), - mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), - ], - ssr: { - noExternal: ['@cedarjs/testing'], - }, - test: { - environment: 'jsdom', - // TODO: Set this to 'false', and let the user configure this on their own - // if this is something they want - // Enables global test APIs like describe, it, expect - globals: true, - // setupFiles: ['./vitest.setup.mjs'], - include: [path.join(rwjsPaths.web.src, '**/*.{test,spec}.{js,jsx,ts,tsx}')], - }, - - resolve: - mode === 'test' - ? { - alias: [ - // Do I need these? - // react: path.join(NODE_MODULES_PATH, 'react'), - // 'react-dom': path.join(NODE_MODULES_PATH, 'react-dom'), - - // Mock implementations - { - find: /^@cedarjs\/router$/, - replacement: path.join( - NODE_MODULES_PATH, - '@cedarjs/testing/web/MockRouter.js', - ), - }, - // { - // find: '@cedarjs/web', - // replacement: path.join( - // NODE_MODULES_PATH, - // '@cedarjs/web/dist/cjs' - // ), - // }, - { - find: /^@cedarjs\/auth$/, - replacement: path.join( - NODE_MODULES_PATH, - '@cedarjs/testing/dist/web/mockAuth.js', - ), - }, - // // '@cedarjs/testing': path.join( - // '~__REDWOOD__USER_ROUTES_FOR_MOCK': { - // find: '', - // replacement: '', - // }, - // // '@cedarjs/testing': path.join( - // // NODE_MODULES_PATH, - // // '@cedarjs/testing/web' - // // ), - // '~__REDWOOD__USER_ROUTES_FOR_MOCK': path.join( - // __dirname, - // 'src', - // 'Routes.tsx' - // ), - ], - } - : {}, -})) From 2e540466c135f520382d07b305b462d03c9efd56 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Mon, 21 Jul 2025 11:08:35 +0200 Subject: [PATCH 064/222] yarn dedupe --- yarn.lock | 263 +----------------------------------------------------- 1 file changed, 1 insertion(+), 262 deletions(-) diff --git a/yarn.lock b/yarn.lock index ba161327bb..d84eeea1ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3986,13 +3986,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/aix-ppc64@npm:0.25.5" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/aix-ppc64@npm:0.25.8" @@ -4021,13 +4014,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/android-arm64@npm:0.25.5" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-arm64@npm:0.25.8" @@ -4056,13 +4042,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/android-arm@npm:0.25.5" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-arm@npm:0.25.8" @@ -4091,13 +4070,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/android-x64@npm:0.25.5" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-x64@npm:0.25.8" @@ -4126,13 +4098,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/darwin-arm64@npm:0.25.5" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/darwin-arm64@npm:0.25.8" @@ -4161,13 +4126,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/darwin-x64@npm:0.25.5" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/darwin-x64@npm:0.25.8" @@ -4196,13 +4154,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/freebsd-arm64@npm:0.25.5" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/freebsd-arm64@npm:0.25.8" @@ -4231,13 +4182,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/freebsd-x64@npm:0.25.5" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/freebsd-x64@npm:0.25.8" @@ -4266,13 +4210,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-arm64@npm:0.25.5" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-arm64@npm:0.25.8" @@ -4301,13 +4238,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-arm@npm:0.25.5" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-arm@npm:0.25.8" @@ -4336,13 +4266,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-ia32@npm:0.25.5" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-ia32@npm:0.25.8" @@ -4371,13 +4294,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-loong64@npm:0.25.5" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-loong64@npm:0.25.8" @@ -4406,13 +4322,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-mips64el@npm:0.25.5" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-mips64el@npm:0.25.8" @@ -4441,13 +4350,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-ppc64@npm:0.25.5" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-ppc64@npm:0.25.8" @@ -4476,13 +4378,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-riscv64@npm:0.25.5" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-riscv64@npm:0.25.8" @@ -4511,13 +4406,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-s390x@npm:0.25.5" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-s390x@npm:0.25.8" @@ -4546,13 +4434,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/linux-x64@npm:0.25.5" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-x64@npm:0.25.8" @@ -4567,13 +4448,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/netbsd-arm64@npm:0.25.5" - conditions: os=netbsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/netbsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/netbsd-arm64@npm:0.25.8" @@ -4602,13 +4476,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/netbsd-x64@npm:0.25.5" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/netbsd-x64@npm:0.25.8" @@ -4623,13 +4490,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/openbsd-arm64@npm:0.25.5" - conditions: os=openbsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/openbsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/openbsd-arm64@npm:0.25.8" @@ -4658,13 +4518,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/openbsd-x64@npm:0.25.5" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/openbsd-x64@npm:0.25.8" @@ -4700,13 +4553,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/sunos-x64@npm:0.25.5" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/sunos-x64@npm:0.25.8" @@ -4735,13 +4581,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/win32-arm64@npm:0.25.5" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-arm64@npm:0.25.8" @@ -4770,13 +4609,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/win32-ia32@npm:0.25.5" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-ia32@npm:0.25.8" @@ -4805,13 +4637,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.5": - version: 0.25.5 - resolution: "@esbuild/win32-x64@npm:0.25.5" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-x64@npm:0.25.8" @@ -16854,7 +16679,7 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:0.25.8": +"esbuild@npm:0.25.8, esbuild@npm:~0.25.0": version: 0.25.8 resolution: "esbuild@npm:0.25.8" dependencies: @@ -17100,92 +16925,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:~0.25.0": - version: 0.25.5 - resolution: "esbuild@npm:0.25.5" - dependencies: - "@esbuild/aix-ppc64": "npm:0.25.5" - "@esbuild/android-arm": "npm:0.25.5" - "@esbuild/android-arm64": "npm:0.25.5" - "@esbuild/android-x64": "npm:0.25.5" - "@esbuild/darwin-arm64": "npm:0.25.5" - "@esbuild/darwin-x64": "npm:0.25.5" - "@esbuild/freebsd-arm64": "npm:0.25.5" - "@esbuild/freebsd-x64": "npm:0.25.5" - "@esbuild/linux-arm": "npm:0.25.5" - "@esbuild/linux-arm64": "npm:0.25.5" - "@esbuild/linux-ia32": "npm:0.25.5" - "@esbuild/linux-loong64": "npm:0.25.5" - "@esbuild/linux-mips64el": "npm:0.25.5" - "@esbuild/linux-ppc64": "npm:0.25.5" - "@esbuild/linux-riscv64": "npm:0.25.5" - "@esbuild/linux-s390x": "npm:0.25.5" - "@esbuild/linux-x64": "npm:0.25.5" - "@esbuild/netbsd-arm64": "npm:0.25.5" - "@esbuild/netbsd-x64": "npm:0.25.5" - "@esbuild/openbsd-arm64": "npm:0.25.5" - "@esbuild/openbsd-x64": "npm:0.25.5" - "@esbuild/sunos-x64": "npm:0.25.5" - "@esbuild/win32-arm64": "npm:0.25.5" - "@esbuild/win32-ia32": "npm:0.25.5" - "@esbuild/win32-x64": "npm:0.25.5" - dependenciesMeta: - "@esbuild/aix-ppc64": - optional: true - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-arm64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-arm64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10c0/aba8cbc11927fa77562722ed5e95541ce2853f67ad7bdc40382b558abc2e0ec57d92ffb820f082ba2047b4ef9f3bc3da068cdebe30dfd3850cfa3827a78d604e - languageName: node - linkType: hard - "escalade@npm:^3.1.1, escalade@npm:^3.2.0": version: 3.2.0 resolution: "escalade@npm:3.2.0" From 9d0bed26fcc4f0a4a2844784191aacd5a2c48f85 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 10:31:03 +0200 Subject: [PATCH 065/222] Update service template to include extension in imports --- .../src/commands/generate/service/templates/test.ts.template | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/generate/service/templates/test.ts.template b/packages/cli/src/commands/generate/service/templates/test.ts.template index e073d8952b..6bbb5e45b5 100644 --- a/packages/cli/src/commands/generate/service/templates/test.ts.template +++ b/packages/cli/src/commands/generate/service/templates/test.ts.template @@ -33,8 +33,8 @@ } %> <% if (prismaImport) { %>import { Prisma, ${prismaModel} } from '@prisma/client'<% } else { %>import type { ${prismaModel} } from '@prisma/client'<% } %> -import { ${pluralCamelName}<% if (crud) { %>,${singularCamelName}, create${singularPascalName}, update${singularPascalName}, delete${singularPascalName}<% } %> } from './${pluralCamelName}' -import type { StandardScenario } from './${pluralCamelName}.scenarios' +import { ${pluralCamelName}<% if (crud) { %>,${singularCamelName}, create${singularPascalName}, update${singularPascalName}, delete${singularPascalName}<% } %> } from './${pluralCamelName}.js' +import type { StandardScenario } from './${pluralCamelName}.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. From cb656924a8a3394fb137baeb793da3acba14e419 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 10:31:30 +0200 Subject: [PATCH 066/222] only run vitest for changed files by default --- packages/cli/src/commands/testHandler.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 626f6f97f4..1a7e6a5aa2 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -66,6 +66,7 @@ export const handler = async ({ ...forwardJestFlags, collectCoverage ? '--collectCoverage' : null, '--passWithNoTests', + '--changed', ].filter((flagOrValue) => flagOrValue !== null) // Filter out nulls, not booleans because user may have passed a --something false flag // If the user wants to watch, set the proper watch flag based on what kind of repo this is From 677e9125a83a003cd3f74b4962d0b3ab8a2d607c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 11:02:54 +0200 Subject: [PATCH 067/222] More work to support using vitest in Cedar apps --- packages/testing/package.json | 11 + packages/testing/src/api/directUrlHelpers.ts | 12 +- packages/testing/src/api/globalSetup.ts | 49 +++ packages/testing/src/api/mockContext.ts | 29 ++ packages/testing/src/vitest/index.ts | 2 +- .../vite-plugin-auto-import-mock-functions.ts | 25 -- .../src/vitest/vite-plugin-auto-import.ts | 60 +++ .../testing/src/vitest/vitest-api.config.ts | 41 ++ .../testing/src/vitest/vitest-api.setup.ts | 407 ++++++++++++++++++ packages/vite/src/index.ts | 2 + packages/vite/src/lib/getMergedConfig.ts | 2 + 11 files changed, 610 insertions(+), 30 deletions(-) create mode 100644 packages/testing/src/api/globalSetup.ts create mode 100644 packages/testing/src/api/mockContext.ts delete mode 100644 packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts create mode 100644 packages/testing/src/vitest/vite-plugin-auto-import.ts create mode 100644 packages/testing/src/vitest/vitest-api.config.ts create mode 100644 packages/testing/src/vitest/vitest-api.setup.ts diff --git a/packages/testing/package.json b/packages/testing/package.json index 798113ad63..65de241f00 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -84,6 +84,17 @@ "types": "./dist/cjs/web/MockRouter.d.ts", "default": "./dist/cjs/web/MockRouter.js" } + }, + "./mockContext": { + "// TODO": "./is the mockContext export still needed?", + "import": { + "types": "./dist/api/mockContext.d.ts", + "default": "./dist/api/mockContext.js" + }, + "require": { + "types": "./dist/cjs/api/mockContext.d.ts", + "default": "./dist/cjs/api/mockContext.js" + } } }, "files": [ diff --git a/packages/testing/src/api/directUrlHelpers.ts b/packages/testing/src/api/directUrlHelpers.ts index 2c80022447..da95cfec04 100644 --- a/packages/testing/src/api/directUrlHelpers.ts +++ b/packages/testing/src/api/directUrlHelpers.ts @@ -17,16 +17,20 @@ export function checkAndReplaceDirectUrl( } // If it is, set its env var to the test equivalent. - const directUrlEnvMatch = directUrl[0].match(BETWEEN_PARENTHESES_REGEXP) //[2] + const directUrlEnvMatch = directUrl[0].match(BETWEEN_PARENTHESES_REGEXP) - // This is mostly to please TS. But it's good to be safe because in this case we want to be 100% correct. + // This is mostly to please TS. But it's good to be safe because in this case + // we want to be 100% correct. if (!directUrlEnvMatch) { throw new Error( - 'Error parsing `directUrl` from schema.prisma. Proceeding with this env var could be dangerous. Please check your schema.prisma file; if everything looks ok, file an issue.', + 'Error parsing `directUrl` from schema.prisma. Proceeding with this ' + + 'env var could be dangerous. Please check your schema.prisma file; ' + + 'if everything looks ok, file an issue.', ) } - // `directUrlEnvMatch` look something like `["(DIRECT_URL)", "", "DIRECT_URL"]`. We want the third element. + // `directUrlEnvMatch` look something like + // `["(DIRECT_URL)", "", "DIRECT_URL"]`. We want the third element. const directUrlEnv = directUrlEnvMatch[2] process.env[directUrlEnv] = diff --git a/packages/testing/src/api/globalSetup.ts b/packages/testing/src/api/globalSetup.ts new file mode 100644 index 0000000000..0277b31261 --- /dev/null +++ b/packages/testing/src/api/globalSetup.ts @@ -0,0 +1,49 @@ +import { getSchema } from '@prisma/internals' +import 'dotenv-defaults/config' +import execa from 'execa' + +import { getPaths } from '@cedarjs/project-config' + +import { getDefaultDb, checkAndReplaceDirectUrl } from './directUrlHelpers.js' + +const cedarPaths = getPaths() + +// TODO: ⛔️ Figure out how often this runs and compare it with the setup +// function in CedarApiVitestEnv. Can we maybe just use one of the two? Or do we +// need both? +export async function setup() { + if (process.env.SKIP_DB_PUSH === '1') { + return + } + + const defaultDb = getDefaultDb(cedarPaths.base) + + process.env.DATABASE_URL = process.env.TEST_DATABASE_URL || defaultDb + + // NOTE: This is a workaround to get the directUrl from the schema + // Instead of using the schema, we can use the config file + // const prismaConfig = await getConfig(rwjsPaths.api.dbSchema) + // and then check for the prismaConfig.datasources[0].directUrl + const prismaSchema = (await getSchema(cedarPaths.api.dbSchema)).toString() + + const directUrlEnvVar = checkAndReplaceDirectUrl(prismaSchema, defaultDb) + + const command = + process.env.TEST_DATABASE_STRATEGY === 'reset' + ? ['prisma', 'migrate', 'reset', '--force', '--skip-seed'] + : ['prisma', 'db', 'push', '--force-reset', '--accept-data-loss'] + + const directUrlDefinition = directUrlEnvVar + ? { [directUrlEnvVar]: process.env[directUrlEnvVar] } + : {} + + execa.sync(`yarn rw`, command, { + cwd: cedarPaths.api.base, + stdio: 'inherit', + shell: true, + env: { + DATABASE_URL: process.env.DATABASE_URL, + ...directUrlDefinition, + }, + }) +} diff --git a/packages/testing/src/api/mockContext.ts b/packages/testing/src/api/mockContext.ts new file mode 100644 index 0000000000..04e2b3ade4 --- /dev/null +++ b/packages/testing/src/api/mockContext.ts @@ -0,0 +1,29 @@ +const mockContextStore = new Map() +const mockContext = new Proxy( + {}, + { + get: (_target, prop) => { + // Handle toJSON() calls, i.e. JSON.stringify(context) + if (prop === 'toJSON') { + return () => mockContextStore.get('context') + } + return mockContextStore.get('context')[prop] + }, + set: (_target, prop, value) => { + const ctx = mockContextStore.get('context') + ctx[prop] = value + return true + }, + }, +) + +// eslint-disable-next-line @typescript-eslint/no-empty-object-type +export interface GlobalContext extends Record {} + +export const context = mockContext + +export const setContext = (newContext: GlobalContext): GlobalContext => { + console.log('calling mocked setContext') + mockContextStore.set('context', newContext) + return mockContext +} diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 94c480fc40..6ff8eab37d 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -2,4 +2,4 @@ export { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router-import-transform.js' export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' -export { autoImportMockFunctionsPlugin } from './vite-plugin-auto-import-mock-functions.js' +export { autoImportPlugin } from './vite-plugin-auto-import.js' diff --git a/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts b/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts deleted file mode 100644 index a437c577a0..0000000000 --- a/packages/testing/src/vitest/vite-plugin-auto-import-mock-functions.ts +++ /dev/null @@ -1,25 +0,0 @@ -import autoImport from 'unplugin-auto-import/vite' - -export function autoImportMockFunctionsPlugin() { - return autoImport({ - // targets to transform - include: [ - /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx - ], - - // global imports to register - imports: [ - { - '@cedarjs/testing/web': [ - 'mockGraphQLQuery', - 'mockGraphQLMutation', - 'mockCurrentUser', - ], - }, - ], - - // We provide our mocking types elsewhere and so don't need this plugin to - // generate them. - dts: false, - }) -} diff --git a/packages/testing/src/vitest/vite-plugin-auto-import.ts b/packages/testing/src/vitest/vite-plugin-auto-import.ts new file mode 100644 index 0000000000..64c403674c --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-auto-import.ts @@ -0,0 +1,60 @@ +import autoImport from 'unplugin-auto-import/vite' + +export function autoImportPlugin() { + return [ + autoImport({ + // targets to transform + include: [ + /web\/src\/.*\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + ], + + // global imports to register + imports: [ + { + '@cedarjs/testing/web': [ + 'mockGraphQLQuery', + 'mockGraphQLMutation', + 'mockCurrentUser', + ], + }, + ], + + // We provide our mocking types elsewhere and so don't need this plugin to + // generate them. + dts: false, + }), + + autoImport({ + // targets to transform + include: [ + /api\/src\/.*\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + ], + + // global imports to register + imports: [ + // import { mockContext, mockHttpEvent, mockSignedWebhook } from '@cedarjs/testing/api'; + { + '@cedarjs/testing/api': [ + 'mockContext', + 'mockHttpEvent', + 'mockSignedWebhook', + ], + }, + // import gql from 'graphql-tag' + { + 'graphql-tag': ['gql'], + }, + // import { context } from '@cedarjs/context' + { + '@cedarjs/context': ['context'], + }, + ], + + // We provide our mocking types elsewhere and so don't need this plugin to + // generate them. + // TODO: Maybe we should have it at least generate the types for the gql + // import? (Or do we already provide that some other way?) + dts: false, + }), + ] +} diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts new file mode 100644 index 0000000000..65e9751488 --- /dev/null +++ b/packages/testing/src/vitest/vitest-api.config.ts @@ -0,0 +1,41 @@ +import path from 'node:path' + +import { defineConfig } from 'vitest/config' + +import { autoImportPlugin } from '@cedarjs/testing/vitest' +import { + cedarjsDirectoryNamedImportPlugin, + getEnvVarDefinitions, +} from '@cedarjs/vite' + +export default defineConfig({ + plugins: [ + // I hate to do this, but it's the pragmatic solution for now. The underlying + // issue is a version missmatch between Vite in CedarJS and Vite in the plugin + // @ts-expect-error - version mismatch + autoImportPlugin(), + cedarjsDirectoryNamedImportPlugin(), + ], + define: getEnvVarDefinitions(), + ssr: { + noExternal: ['@cedarjs/testing'], + }, + resolve: { + alias: { + src: path.resolve(__dirname, './src'), + }, + }, + test: { + environment: './src/CedarApiVitestEnv.ts', + // fileParallelism: false, + // fileParallelism doesn't work with vitest projects (which is what we're + // using in the root vitest.config.ts). As a workaround we set poolOptions + // instead, which also shouldn't work, but was suggested by Vitest team + // member AriPerkkio (Hiroshi's answer didn't work). + // https://github.com/vitest-dev/vitest/discussions/7416 + poolOptions: { forks: { singleFork: true } }, + globals: true, + globalSetup: ['./src/globalSetup.ts'], + setupFiles: ['./vitest.setup.ts'], + }, +}) diff --git a/packages/testing/src/vitest/vitest-api.setup.ts b/packages/testing/src/vitest/vitest-api.setup.ts new file mode 100644 index 0000000000..7bd28ac75c --- /dev/null +++ b/packages/testing/src/vitest/vitest-api.setup.ts @@ -0,0 +1,407 @@ +// TODO: Remove this +/* eslint @typescript-eslint/no-explicit-any: 0 */ + +import fs from 'node:fs' +import path from 'node:path' + +import { beforeEach, it, describe, vi, beforeAll } from 'vitest' + +import { getPaths } from '@cedarjs/project-config' +import { defineScenario } from '@cedarjs/testing/api' + +// Attempt to emulate the request context isolation behavior +// This is a little more complicated than it would necessarily need to be +// but we're following the same pattern as in `@cedarjs/context` +const mockContextStore = vi.hoisted(() => new Map()) +const mockContext = vi.hoisted( + () => + new Proxy( + {}, + { + get: (_target, prop) => { + // Handle toJSON() calls, i.e. JSON.stringify(context) + if (prop === 'toJSON') { + return () => mockContextStore.get('context') + } + + return mockContextStore.get('context')[prop] + }, + set: (_target, prop, value) => { + const ctx = mockContextStore.get('context') + ctx[prop] = value + + return true + }, + }, + ), +) + +vi.mock('@cedarjs/context', () => { + return { + context: mockContext, + setContext: (newContext: unknown) => { + mockContextStore.set('context', newContext) + }, + } +}) + +beforeEach(() => { + mockContextStore.set('context', {}) +}) + +declare global { + // eslint-disable-next-line no-var + var mockCurrentUser: (currentUser: Record | null) => void +} + +globalThis.mockCurrentUser = (currentUser: Record | null) => { + mockContextStore.set('context', { currentUser }) +} + +// ==================================== +// Scenario support +// ==================================== + +globalThis.defineScenario = defineScenario + +const cedarPaths = getPaths() + +// Error codes thrown by [MySQL, SQLite, Postgres] when foreign key constraint +// fails on DELETE +const FOREIGN_KEY_ERRORS = [1451, 1811, 23503] +const TEARDOWN_CACHE_PATH = path.join( + cedarPaths.generated.base, + 'scenarioTeardown.json', +) +const DEFAULT_SCENARIO = 'standard' +let teardownOrder: (string | null)[] = [] +let originalTeardownOrder: string[] = [] + +type It = typeof it | typeof it.only +type Describe = typeof describe | typeof describe.only +type TestFunc = (scenarioData: any) => any +type DescribeBlock = (getScenario: () => any) => any + +/** + * Wraps "it" or "test", to seed and teardown the scenario after each test + * This one passes scenario data to the test function + */ +function buildScenario(itFunc: It) { + return ( + ...args: + | [scenarioName: string, testName: string, testFunc: TestFunc] + | [testName: string, testFunc: TestFunc] + ) => { + let scenarioName: string + let testName: string + let testFunc: TestFunc + + if (args.length === 3) { + ;[scenarioName, testName, testFunc] = args + } else if (args.length === 2) { + scenarioName = DEFAULT_SCENARIO + ;[testName, testFunc] = args + } else { + throw new Error('scenario() requires 2 or 3 arguments') + } + + return itFunc(testName, async (ctx) => { + const testPath = ctx.task.file.filepath + const { scenario } = await loadScenarios(testPath, scenarioName) + + const scenarioData = await seedScenario(scenario) + try { + console.log(`Running test ${testName} with scenario ${scenarioName}`) + const result = await testFunc(scenarioData) + + return result + } finally { + console.log( + `Cleaning up after test ${testName} with scenario ${scenarioName}`, + ) + // Make sure to cleanup, even if test fails + await teardown() + } + }) + } +} + +/** + * This creates a describe() block that will seed the scenario ONCE before all tests in the block + * Note that you need to use the getScenario() function to get the data. + */ +function buildDescribeScenario(describeFunc: Describe) { + return ( + ...args: [string, string, DescribeBlock] | [string, DescribeBlock] + ) => { + let scenarioName: string + let describeBlockName: string + let describeBlock: DescribeBlock + + if (args.length === 3) { + ;[scenarioName, describeBlockName, describeBlock] = args + } else if (args.length === 2) { + scenarioName = DEFAULT_SCENARIO + ;[describeBlockName, describeBlock] = args + } else { + throw new Error('describeScenario() requires 2 or 3 arguments') + } + + return describeFunc(describeBlockName, () => { + let scenarioData: Record + + beforeAll(async (ctx) => { + const testPath = ctx.file.filepath + const { scenario } = await loadScenarios(testPath, scenarioName) + scenarioData = await seedScenario(scenario) + }) + + afterAll(async () => { + await teardown() + }) + + const getScenario = () => scenarioData + + describeBlock(getScenario) + }) + } +} + +async function configureTeardown() { + if (!wasDbUsed()) { + return + } + + const { getDMMF, getSchema } = await import('@prisma/internals') + + // @NOTE prisma utils are available in cli lib/schemaHelpers + // But avoid importing them, to prevent memory leaks in jest + const datamodel = await getSchema(cedarPaths.api.dbSchema) + const schema = await getDMMF({ datamodel }) + const schemaModels = schema.datamodel.models.map((m) => { + return m.dbName || m.name + }) + + console.log('schema models', schemaModels) + + // check if pre-defined delete order already exists and if so, use it to start + if (fs.existsSync(TEARDOWN_CACHE_PATH)) { + teardownOrder = JSON.parse(fs.readFileSync(TEARDOWN_CACHE_PATH).toString()) + } + + // check the number of models in case we've added or removed any models since + // cache was built + if (teardownOrder.length !== schemaModels.length) { + teardownOrder = schemaModels + } + + console.log('teardown order', teardownOrder) + + // keep a copy of the original order to compare against + originalTeardownOrder = deepCopy(teardownOrder) +} + +beforeAll(async () => { + await configureTeardown() +}) + +async function teardown() { + if (!wasDbUsed()) { + return + } + + const quoteStyle = await getQuoteStyle() + const projectDb = await getProjectDb() + + for (const modelName of teardownOrder) { + try { + const query = `DELETE FROM ${quoteStyle}${modelName}${quoteStyle}` + console.log(`Deleting ${modelName} using query: ${query}`) + await projectDb.$executeRawUnsafe(query) + } catch (e) { + console.error('teardown error\n', e) + const match = isErrorWithMessage(e) && e.message.match(/Code: `(\d+)`/) + + if (match && FOREIGN_KEY_ERRORS.includes(parseInt(match[1]))) { + const index = teardownOrder.indexOf(modelName) + teardownOrder[index] = null + teardownOrder.push(modelName) + } else { + throw e + } + } + } + + // remove nulls + teardownOrder = teardownOrder.filter((val) => val) + + // if the order of delete changed, write out the cached file again + if (!isIdenticalArray(teardownOrder, originalTeardownOrder)) { + originalTeardownOrder = deepCopy(teardownOrder) + fs.writeFileSync(TEARDOWN_CACHE_PATH, JSON.stringify(teardownOrder)) + } +} + +const seedScenario = async (scenario: Record) => { + if (scenario) { + const scenarios: Record = {} + + const projectDb = await getProjectDb() + + for (const [model, namedFixtures] of Object.entries(scenario)) { + scenarios[model] = {} + + for (const [name, createArgs] of Object.entries(namedFixtures)) { + if (typeof createArgs === 'function') { + scenarios[model][name] = await projectDb[model].create( + createArgs(scenarios), + ) + } else { + scenarios[model][name] = await projectDb[model].create(createArgs) + } + } + } + + return scenarios + } else { + return {} + } +} + +async function loadScenarios(testPath: string, scenarioName: string) { + const testFileDir = path.parse(testPath) + // e.g. ['comments', 'test'] or ['signup', 'state', 'machine', 'test'] + const testFileNameParts = testFileDir.name.split('.') + const testFilePath = `${testFileDir.dir}/${testFileNameParts + .slice(0, testFileNameParts.length - 1) + .join('.')}.scenarios` + let allScenarios: Record | undefined + let scenario: any + + try { + allScenarios = await import(testFilePath) + } catch (e) { + // ignore error if scenario file not found, otherwise re-throw + if (isErrorWithCode(e)) { + if (e instanceof Error) { + throw e + } else { + console.error('unexpected error type', e) + // eslint-disable-next-line + throw e + } + } + } + + if (allScenarios) { + if (allScenarios[scenarioName]) { + scenario = allScenarios[scenarioName] + } else { + throw new Error( + `UndefinedScenario: There is no scenario named "${scenarioName}" in ${testFilePath}.{js,ts}`, + ) + } + } + return { scenario } +} + +/** + * All these hooks run in the VM/Context that the test runs in since we're using + * "setupAfterEnv". + * There's a new context for each test-suite i.e. each test file + * + * Doing this means if the db isn't used in the current test context, no need to + * do any of the teardown logic - allowing simple tests to run faster + * At the same time, if the db is used, disconnecting it in this context + * prevents connection limit errors. + * Just disconnecting db in jest-preset is not enough, because the Prisma client + * is created in a different context. + */ +const wasDbUsed = () => { + // This code doesn't work in an ESM environment. There is no require, and even + // if I could create one, there's no require cache to look for db imports in. + // + // Some alternative solutions I could try implementing: + // - Use vitest to mock the db lib import, wrapping it with a simple proxy + // that records usage + // - Use a vite plugin to replace the db lib import with a mock that records + // usage + // + // For now I'm just returning `true`, which isn't as effective as it could be + // but should at least unblock me for now + // + // try { + // const libDbPath = require.resolve(`${cedarPaths.api.lib}/db`) + // return Object.keys(require.cache).some((module) => { + // return module === libDbPath + // }) + // } catch { + // // If db wasn't resolved, no point trying to perform db resets + // return false + // } + + // TODO: ⛔️ Be smarter about this + return true +} + +let quoteStyle: string +// determine what kind of quotes are needed around table names in raw SQL +async function getQuoteStyle() { + const { getConfig: getPrismaConfig, getSchema } = await import( + '@prisma/internals' + ) + + // @NOTE prisma utils are available in cli lib/schemaHelpers + // But avoid importing them, to prevent memory leaks in jest + const datamodel = await getSchema(cedarPaths.api.dbSchema) + + if (!quoteStyle) { + const config = await getPrismaConfig({ + datamodel, + }) + + switch (config.datasources?.[0]?.provider) { + case 'mysql': + quoteStyle = '`' + break + default: + quoteStyle = '"' + } + } + + return quoteStyle +} + +async function getProjectDb() { + const libDb = await import(`${cedarPaths.api.lib}/db`) + + return libDb.db +} + +function isIdenticalArray(a: unknown[], b: unknown[]) { + return JSON.stringify(a) === JSON.stringify(b) +} + +function deepCopy(obj: unknown[]) { + return JSON.parse(JSON.stringify(obj)) +} + +function isErrorWithMessage(e: unknown): e is { message: string } { + return ( + !!e && + typeof e === 'object' && + 'message' in e && + typeof e.message === 'string' + ) +} + +function isErrorWithCode(e: unknown): e is { code: string } { + return ( + !!e && typeof e === 'object' && 'code' in e && typeof e.code === 'string' + ) +} + +globalThis.scenario = buildScenario(it) +globalThis.scenario.only = buildScenario(it.only) +globalThis.describeScenario = buildDescribeScenario(describe) +globalThis.describeScenario.only = buildDescribeScenario(describe.only) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 7822e24bd4..11d9ed111e 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -31,6 +31,8 @@ export { cedarTransformJsAsJsx } from './plugins/vite-plugin-jsx-loader.js' export { cedarMergedConfig } from './plugins/vite-plugin-merged-config.js' export { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provider.js' +export { getEnvVarDefinitions } from './lib/envVarDefinitions.js' + type PluginOptions = { mode: string | undefined } diff --git a/packages/vite/src/lib/getMergedConfig.ts b/packages/vite/src/lib/getMergedConfig.ts index 49a94d3900..85ba5b4e25 100644 --- a/packages/vite/src/lib/getMergedConfig.ts +++ b/packages/vite/src/lib/getMergedConfig.ts @@ -146,6 +146,8 @@ export function getMergedConfig(rwConfig: Config, rwPaths: Paths) { }, }, ssr: { + // `@cedarjs/testing` is not externalized in order to support + // `import.meta.glob`, which we use in one of the files in the package noExternal: env.mode == 'test' ? ['@cedarjs/testing'] : [], }, resolve: { From a4027fa01bcd0268d8440ead5512e04210abaaf6 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 15:28:19 +0200 Subject: [PATCH 068/222] make it build --- .../testing/config/jest/api/jest.setup.ts | 4 +- packages/testing/global.d.ts | 30 +++++++------- packages/testing/src/vitest/index.ts | 1 + .../vitest/vite-plugin-track-db-imports.ts | 16 ++++++++ .../testing/src/vitest/vitest-api.config.ts | 3 +- .../testing/src/vitest/vitest-api.setup.ts | 39 ++++++++++++++++--- packages/vite/src/index.ts | 4 +- 7 files changed, 73 insertions(+), 24 deletions(-) create mode 100644 packages/testing/src/vitest/vite-plugin-track-db-imports.ts diff --git a/packages/testing/config/jest/api/jest.setup.ts b/packages/testing/config/jest/api/jest.setup.ts index 23fbf54881..3511eb83a0 100644 --- a/packages/testing/config/jest/api/jest.setup.ts +++ b/packages/testing/config/jest/api/jest.setup.ts @@ -7,7 +7,7 @@ type SuiteAPI = jest.Describe declare global { // eslint-disable-next-line no-var - var mockCurrentUser: (currentUser: Record | null) => void + var defineScenario: (currentUser: Record | null) => void } // @NOTE without these imports in the setup file, mockCurrentUser @@ -306,7 +306,7 @@ jest.mock('@cedarjs/context', () => { beforeEach(() => { mockContextStore.set('context', {}) }) -global.mockCurrentUser = (currentUser: Record | null) => { +global.defineScenario = (currentUser: Record | null) => { mockContextStore.set('context', { currentUser }) } diff --git a/packages/testing/global.d.ts b/packages/testing/global.d.ts index 4b1f7b3efa..69040f390b 100644 --- a/packages/testing/global.d.ts +++ b/packages/testing/global.d.ts @@ -1,3 +1,5 @@ +// TODO: Is this used? + /* eslint-disable no-var */ import type { Global as jest } from '@jest/types' type TestAPI = jest.It @@ -11,20 +13,20 @@ import type { import type { DefineScenario } from './src/api/scenario.ts' declare global { - var scenario: ( - ...args: - | [ - scenarioName: string, - testName: string, - testFunc: (scenarioData: any) => any, - ] - | [testName: string, testFunc: (scenarioData: any) => any] - ) => void - var describeScenario: ( - ...args: - | [string, string, (getScenario: () => any) => any] - | [string, (getScenario: () => any) => any] - ) => ReturnType + // var scenario: ( + // ...args: + // | [ + // scenarioName: string, + // testName: string, + // testFunc: (scenarioData: any) => any, + // ] + // | [testName: string, testFunc: (scenarioData: any) => any] + // ) => void + // var describeScenario: ( + // ...args: + // | [string, string, (getScenario: () => any) => any] + // | [string, (getScenario: () => any) => any] + // ) => ReturnType var describe: SuiteAPI var it: TestAPI var testPath: string diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 6ff8eab37d..53154dfaf7 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -3,3 +3,4 @@ export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeR export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router-import-transform.js' export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' export { autoImportPlugin } from './vite-plugin-auto-import.js' +export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' diff --git a/packages/testing/src/vitest/vite-plugin-track-db-imports.ts b/packages/testing/src/vitest/vite-plugin-track-db-imports.ts new file mode 100644 index 0000000000..288c1a6fd8 --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-track-db-imports.ts @@ -0,0 +1,16 @@ +import type { Plugin } from 'vite' + +export function trackDbImportsPlugin(): Plugin { + return { + name: 'db-import-tracker', + transform(code, id) { + if (id.match(/src\/lib\/db\.(js|ts)$/)) { + // Inserting the code last (instead of at the top) works nicer with + // sourcemaps + return code + '\n\n;globalThis.__cedarjs_db_imported__ = true;' + } + + return code + }, + } +} diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts index 65e9751488..b0da19418a 100644 --- a/packages/testing/src/vitest/vitest-api.config.ts +++ b/packages/testing/src/vitest/vitest-api.config.ts @@ -12,7 +12,8 @@ export default defineConfig({ plugins: [ // I hate to do this, but it's the pragmatic solution for now. The underlying // issue is a version missmatch between Vite in CedarJS and Vite in the plugin - // @ts-expect-error - version mismatch + // TODO: How does this affect projects using this? + // _@ts-expect-error - version mismatch autoImportPlugin(), cedarjsDirectoryNamedImportPlugin(), ], diff --git a/packages/testing/src/vitest/vitest-api.setup.ts b/packages/testing/src/vitest/vitest-api.setup.ts index 7bd28ac75c..e83a2202a4 100644 --- a/packages/testing/src/vitest/vitest-api.setup.ts +++ b/packages/testing/src/vitest/vitest-api.setup.ts @@ -8,6 +8,7 @@ import { beforeEach, it, describe, vi, beforeAll } from 'vitest' import { getPaths } from '@cedarjs/project-config' import { defineScenario } from '@cedarjs/testing/api' +import type { DefineScenario } from '@cedarjs/testing/api' // Attempt to emulate the request context isolation behavior // This is a little more complicated than it would necessarily need to be @@ -62,7 +63,12 @@ globalThis.mockCurrentUser = (currentUser: Record | null) => { // Scenario support // ==================================== -globalThis.defineScenario = defineScenario +declare global { + // eslint-disable-next-line no-var + var defineScenario: DefineScenario +} + +global.defineScenario = defineScenario const cedarPaths = getPaths() @@ -401,7 +407,30 @@ function isErrorWithCode(e: unknown): e is { code: string } { ) } -globalThis.scenario = buildScenario(it) -globalThis.scenario.only = buildScenario(it.only) -globalThis.describeScenario = buildDescribeScenario(describe) -globalThis.describeScenario.only = buildDescribeScenario(describe.only) +interface GlobalScenario { + (...args: [string, string, TestFunc] | [string, TestFunc]): ReturnType + only?: ( + ...args: [string, string, TestFunc] | [string, TestFunc] + ) => ReturnType +} + +interface DescribeScenario { + ( + ...args: [string, string, DescribeBlock] | [string, DescribeBlock] + ): ReturnType + only?: ( + ...args: [string, string, DescribeBlock] | [string, DescribeBlock] + ) => ReturnType +} + +declare global { + // eslint-disable-next-line no-var + var scenario: GlobalScenario + // eslint-disable-next-line no-var + var describeScenario: DescribeScenario +} + +global.scenario = buildScenario(it) +global.scenario.only = buildScenario(it.only) +global.describeScenario = buildDescribeScenario(describe) +global.describeScenario.only = buildDescribeScenario(describe.only) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 11d9ed111e..b98eb533e6 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -4,7 +4,7 @@ import type { PluginOption } from 'vite' import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' import { getConfig } from '@cedarjs/project-config' import { - autoImportMockFunctionsPlugin, + autoImportPlugin, // mockProvidersRoutesPlugin, // mockProvidersRelativeRoutesPathsPlugin, cedarJsRouterImportTransformPlugin, @@ -68,7 +68,7 @@ export function cedar({ mode }: PluginOptions): PluginOption[] { // mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), mode === 'test' && cedarJsRouterImportTransformPlugin(), mode === 'test' && createAuthImportTransformPlugin(), - mode === 'test' && autoImportMockFunctionsPlugin(), + mode === 'test' && autoImportPlugin(), cedarNodePolyfills(), cedarHtmlEnvPlugin(), cedarEntryInjectionPlugin(), From 4550f5760a416c39f97ae146dbf2f459a74e7f51 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 15:30:11 +0200 Subject: [PATCH 069/222] fix eslint version mismatch --- packages/cli/package.json | 2 +- yarn.lock | 263 +------------------------------------- 2 files changed, 2 insertions(+), 263 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index fb102c0bf9..983bec85b3 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -63,7 +63,7 @@ "dotenv-defaults": "5.0.2", "enquirer": "2.4.1", "envinfo": "7.14.0", - "esbuild": "0.25.0", + "esbuild": "0.25.8", "execa": "5.1.1", "fast-glob": "3.3.3", "fs-extra": "11.2.0", diff --git a/yarn.lock b/yarn.lock index d84eeea1ba..9020e2f390 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2747,7 +2747,7 @@ __metadata: dotenv-defaults: "npm:5.0.2" enquirer: "npm:2.4.1" envinfo: "npm:7.14.0" - esbuild: "npm:0.25.0" + esbuild: "npm:0.25.8" execa: "npm:5.1.1" fast-glob: "npm:3.3.3" fs-extra: "npm:11.2.0" @@ -3979,13 +3979,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/aix-ppc64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/aix-ppc64@npm:0.25.0" - conditions: os=aix & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/aix-ppc64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/aix-ppc64@npm:0.25.8" @@ -4007,13 +4000,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-arm64@npm:0.25.0" - conditions: os=android & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/android-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-arm64@npm:0.25.8" @@ -4035,13 +4021,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-arm@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-arm@npm:0.25.0" - conditions: os=android & cpu=arm - languageName: node - linkType: hard - "@esbuild/android-arm@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-arm@npm:0.25.8" @@ -4063,13 +4042,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/android-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/android-x64@npm:0.25.0" - conditions: os=android & cpu=x64 - languageName: node - linkType: hard - "@esbuild/android-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/android-x64@npm:0.25.8" @@ -4091,13 +4063,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/darwin-arm64@npm:0.25.0" - conditions: os=darwin & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/darwin-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/darwin-arm64@npm:0.25.8" @@ -4119,13 +4084,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/darwin-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/darwin-x64@npm:0.25.0" - conditions: os=darwin & cpu=x64 - languageName: node - linkType: hard - "@esbuild/darwin-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/darwin-x64@npm:0.25.8" @@ -4147,13 +4105,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/freebsd-arm64@npm:0.25.0" - conditions: os=freebsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/freebsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/freebsd-arm64@npm:0.25.8" @@ -4175,13 +4126,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/freebsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/freebsd-x64@npm:0.25.0" - conditions: os=freebsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/freebsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/freebsd-x64@npm:0.25.8" @@ -4203,13 +4147,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-arm64@npm:0.25.0" - conditions: os=linux & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/linux-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-arm64@npm:0.25.8" @@ -4231,13 +4168,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-arm@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-arm@npm:0.25.0" - conditions: os=linux & cpu=arm - languageName: node - linkType: hard - "@esbuild/linux-arm@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-arm@npm:0.25.8" @@ -4259,13 +4189,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ia32@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-ia32@npm:0.25.0" - conditions: os=linux & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/linux-ia32@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-ia32@npm:0.25.8" @@ -4287,13 +4210,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-loong64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-loong64@npm:0.25.0" - conditions: os=linux & cpu=loong64 - languageName: node - linkType: hard - "@esbuild/linux-loong64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-loong64@npm:0.25.8" @@ -4315,13 +4231,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-mips64el@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-mips64el@npm:0.25.0" - conditions: os=linux & cpu=mips64el - languageName: node - linkType: hard - "@esbuild/linux-mips64el@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-mips64el@npm:0.25.8" @@ -4343,13 +4252,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-ppc64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-ppc64@npm:0.25.0" - conditions: os=linux & cpu=ppc64 - languageName: node - linkType: hard - "@esbuild/linux-ppc64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-ppc64@npm:0.25.8" @@ -4371,13 +4273,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-riscv64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-riscv64@npm:0.25.0" - conditions: os=linux & cpu=riscv64 - languageName: node - linkType: hard - "@esbuild/linux-riscv64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-riscv64@npm:0.25.8" @@ -4399,13 +4294,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-s390x@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-s390x@npm:0.25.0" - conditions: os=linux & cpu=s390x - languageName: node - linkType: hard - "@esbuild/linux-s390x@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-s390x@npm:0.25.8" @@ -4427,13 +4315,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/linux-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/linux-x64@npm:0.25.0" - conditions: os=linux & cpu=x64 - languageName: node - linkType: hard - "@esbuild/linux-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/linux-x64@npm:0.25.8" @@ -4441,13 +4322,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/netbsd-arm64@npm:0.25.0" - conditions: os=netbsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/netbsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/netbsd-arm64@npm:0.25.8" @@ -4469,13 +4343,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/netbsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/netbsd-x64@npm:0.25.0" - conditions: os=netbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/netbsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/netbsd-x64@npm:0.25.8" @@ -4483,13 +4350,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/openbsd-arm64@npm:0.25.0" - conditions: os=openbsd & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/openbsd-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/openbsd-arm64@npm:0.25.8" @@ -4511,13 +4371,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/openbsd-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/openbsd-x64@npm:0.25.0" - conditions: os=openbsd & cpu=x64 - languageName: node - linkType: hard - "@esbuild/openbsd-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/openbsd-x64@npm:0.25.8" @@ -4546,13 +4399,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/sunos-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/sunos-x64@npm:0.25.0" - conditions: os=sunos & cpu=x64 - languageName: node - linkType: hard - "@esbuild/sunos-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/sunos-x64@npm:0.25.8" @@ -4574,13 +4420,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-arm64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-arm64@npm:0.25.0" - conditions: os=win32 & cpu=arm64 - languageName: node - linkType: hard - "@esbuild/win32-arm64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-arm64@npm:0.25.8" @@ -4602,13 +4441,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-ia32@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-ia32@npm:0.25.0" - conditions: os=win32 & cpu=ia32 - languageName: node - linkType: hard - "@esbuild/win32-ia32@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-ia32@npm:0.25.8" @@ -4630,13 +4462,6 @@ __metadata: languageName: node linkType: hard -"@esbuild/win32-x64@npm:0.25.0": - version: 0.25.0 - resolution: "@esbuild/win32-x64@npm:0.25.0" - conditions: os=win32 & cpu=x64 - languageName: node - linkType: hard - "@esbuild/win32-x64@npm:0.25.8": version: 0.25.8 resolution: "@esbuild/win32-x64@npm:0.25.8" @@ -16593,92 +16418,6 @@ __metadata: languageName: node linkType: hard -"esbuild@npm:0.25.0": - version: 0.25.0 - resolution: "esbuild@npm:0.25.0" - dependencies: - "@esbuild/aix-ppc64": "npm:0.25.0" - "@esbuild/android-arm": "npm:0.25.0" - "@esbuild/android-arm64": "npm:0.25.0" - "@esbuild/android-x64": "npm:0.25.0" - "@esbuild/darwin-arm64": "npm:0.25.0" - "@esbuild/darwin-x64": "npm:0.25.0" - "@esbuild/freebsd-arm64": "npm:0.25.0" - "@esbuild/freebsd-x64": "npm:0.25.0" - "@esbuild/linux-arm": "npm:0.25.0" - "@esbuild/linux-arm64": "npm:0.25.0" - "@esbuild/linux-ia32": "npm:0.25.0" - "@esbuild/linux-loong64": "npm:0.25.0" - "@esbuild/linux-mips64el": "npm:0.25.0" - "@esbuild/linux-ppc64": "npm:0.25.0" - "@esbuild/linux-riscv64": "npm:0.25.0" - "@esbuild/linux-s390x": "npm:0.25.0" - "@esbuild/linux-x64": "npm:0.25.0" - "@esbuild/netbsd-arm64": "npm:0.25.0" - "@esbuild/netbsd-x64": "npm:0.25.0" - "@esbuild/openbsd-arm64": "npm:0.25.0" - "@esbuild/openbsd-x64": "npm:0.25.0" - "@esbuild/sunos-x64": "npm:0.25.0" - "@esbuild/win32-arm64": "npm:0.25.0" - "@esbuild/win32-ia32": "npm:0.25.0" - "@esbuild/win32-x64": "npm:0.25.0" - dependenciesMeta: - "@esbuild/aix-ppc64": - optional: true - "@esbuild/android-arm": - optional: true - "@esbuild/android-arm64": - optional: true - "@esbuild/android-x64": - optional: true - "@esbuild/darwin-arm64": - optional: true - "@esbuild/darwin-x64": - optional: true - "@esbuild/freebsd-arm64": - optional: true - "@esbuild/freebsd-x64": - optional: true - "@esbuild/linux-arm": - optional: true - "@esbuild/linux-arm64": - optional: true - "@esbuild/linux-ia32": - optional: true - "@esbuild/linux-loong64": - optional: true - "@esbuild/linux-mips64el": - optional: true - "@esbuild/linux-ppc64": - optional: true - "@esbuild/linux-riscv64": - optional: true - "@esbuild/linux-s390x": - optional: true - "@esbuild/linux-x64": - optional: true - "@esbuild/netbsd-arm64": - optional: true - "@esbuild/netbsd-x64": - optional: true - "@esbuild/openbsd-arm64": - optional: true - "@esbuild/openbsd-x64": - optional: true - "@esbuild/sunos-x64": - optional: true - "@esbuild/win32-arm64": - optional: true - "@esbuild/win32-ia32": - optional: true - "@esbuild/win32-x64": - optional: true - bin: - esbuild: bin/esbuild - checksum: 10c0/5767b72da46da3cfec51661647ec850ddbf8a8d0662771139f10ef0692a8831396a0004b2be7966cecdb08264fb16bdc16290dcecd92396fac5f12d722fa013d - languageName: node - linkType: hard - "esbuild@npm:0.25.8, esbuild@npm:~0.25.0": version: 0.25.8 resolution: "esbuild@npm:0.25.8" From e01e1c1fce8e73d5ec7154395b6953e5561dbc3d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 15:44:47 +0200 Subject: [PATCH 070/222] rename autoImportsPlugin --- packages/testing/src/vitest/index.ts | 2 +- packages/testing/src/vitest/vite-plugin-auto-import.ts | 2 +- packages/testing/src/vitest/vitest-api.config.ts | 4 ++-- packages/vite/src/index.ts | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 53154dfaf7..8d0f405326 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -2,5 +2,5 @@ export { mockProvidersRoutesPlugin } from './mockProvidersRoutesPlugin.js' export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeRoutesPathsPlugin.js' export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router-import-transform.js' export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' -export { autoImportPlugin } from './vite-plugin-auto-import.js' +export { autoImportsPlugin } from './vite-plugin-auto-import.js' export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' diff --git a/packages/testing/src/vitest/vite-plugin-auto-import.ts b/packages/testing/src/vitest/vite-plugin-auto-import.ts index 64c403674c..bcc42f66d6 100644 --- a/packages/testing/src/vitest/vite-plugin-auto-import.ts +++ b/packages/testing/src/vitest/vite-plugin-auto-import.ts @@ -1,6 +1,6 @@ import autoImport from 'unplugin-auto-import/vite' -export function autoImportPlugin() { +export function autoImportsPlugin() { return [ autoImport({ // targets to transform diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts index b0da19418a..4d0b102b60 100644 --- a/packages/testing/src/vitest/vitest-api.config.ts +++ b/packages/testing/src/vitest/vitest-api.config.ts @@ -2,7 +2,7 @@ import path from 'node:path' import { defineConfig } from 'vitest/config' -import { autoImportPlugin } from '@cedarjs/testing/vitest' +import { autoImportsPlugin } from '@cedarjs/testing/vitest' import { cedarjsDirectoryNamedImportPlugin, getEnvVarDefinitions, @@ -14,7 +14,7 @@ export default defineConfig({ // issue is a version missmatch between Vite in CedarJS and Vite in the plugin // TODO: How does this affect projects using this? // _@ts-expect-error - version mismatch - autoImportPlugin(), + autoImportsPlugin(), cedarjsDirectoryNamedImportPlugin(), ], define: getEnvVarDefinitions(), diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index b98eb533e6..6542e07ca3 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -4,7 +4,7 @@ import type { PluginOption } from 'vite' import { getWebSideDefaultBabelConfig } from '@cedarjs/babel-config' import { getConfig } from '@cedarjs/project-config' import { - autoImportPlugin, + autoImportsPlugin, // mockProvidersRoutesPlugin, // mockProvidersRelativeRoutesPathsPlugin, cedarJsRouterImportTransformPlugin, @@ -68,7 +68,7 @@ export function cedar({ mode }: PluginOptions): PluginOption[] { // mode === 'test' && mockProvidersRelativeRoutesPathsPlugin(), mode === 'test' && cedarJsRouterImportTransformPlugin(), mode === 'test' && createAuthImportTransformPlugin(), - mode === 'test' && autoImportPlugin(), + mode === 'test' && autoImportsPlugin(), cedarNodePolyfills(), cedarHtmlEnvPlugin(), cedarEntryInjectionPlugin(), From 80d34e799517d7699eeaf96359498817c595d618 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 16:05:16 +0200 Subject: [PATCH 071/222] fix vitest version mismatch by using peerdep --- packages/testing/package.json | 7 ++- .../vitest/vite-plugin-track-db-imports.ts | 49 +++++++++++++++++++ .../testing/src/vitest/vitest-api.config.ts | 10 ++-- yarn.lock | 2 + 4 files changed, 61 insertions(+), 7 deletions(-) diff --git a/packages/testing/package.json b/packages/testing/package.json index 65de241f00..920152097a 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -139,7 +139,6 @@ "msw": "1.3.4", "ts-toolbelt": "9.6.0", "unplugin-auto-import": "19.3.0", - "vitest": "3.2.4", "whatwg-fetch": "3.6.20" }, "devDependencies": { @@ -148,7 +147,11 @@ "jsdom": "24.1.3", "publint": "0.3.12", "tsx": "4.20.3", - "typescript": "5.6.2" + "typescript": "5.6.2", + "vitest": "3.2.4" + }, + "peerDependencies": { + "vitest": "3.2.4" }, "gitHead": "3905ed045508b861b495f8d5630d76c7a157d8f1" } diff --git a/packages/testing/src/vitest/vite-plugin-track-db-imports.ts b/packages/testing/src/vitest/vite-plugin-track-db-imports.ts index 288c1a6fd8..bef614e299 100644 --- a/packages/testing/src/vitest/vite-plugin-track-db-imports.ts +++ b/packages/testing/src/vitest/vite-plugin-track-db-imports.ts @@ -14,3 +14,52 @@ export function trackDbImportsPlugin(): Plugin { }, } } + +// This is a version that AI suggested to me +// TODO: There are a few things I want to explore +// - How's resetting the flag working currently? Is it reset when it needs to +// be? Is doing it in configureServer() and buildStart() actually helping? +// - Correct source maps are nice when something is broken in a test. Should +// look into providing the `map` property in the transform result. What does +// `null` mean? +// - That `typeof` guard looks sensible. Should probably add someting like that. +// But maybe throwing an error if it's undefined, so that users can report it +// as an error with the framework +// - Another option for resetting the flag is to have a `afterEach` or +// `afterAll` that does it +// +// { +// name: 'db-import-tracker', +// configureServer() { +// // Reset flag when server starts +// globalThis.__cedarjs_db_imported__ = false; +// }, +// buildStart() { +// // Reset flag when build starts +// globalThis.__cedarjs_db_imported__ = false; +// }, +// transform(code, id) { +// // More comprehensive matching +// const isDbModule = +// /src\/lib\/db\.(js|ts|mjs|cjs)$/.test(id) || +// /src\/lib\/db\/index\.(js|ts|mjs|cjs)$/.test(id); + +// if (isDbModule) { +// // Add some safety checks +// const injectedCode = ` +// // Cedar.js DB import tracker +// if (typeof globalThis !== 'undefined') { +// globalThis.__cedarjs_db_imported__ = true; +// } + +// ${code}`; + +// return { +// code: injectedCode, +// map: null +// }; +// } + +// return null; +// }, +// } diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts index 4d0b102b60..7afbc80a7f 100644 --- a/packages/testing/src/vitest/vitest-api.config.ts +++ b/packages/testing/src/vitest/vitest-api.config.ts @@ -2,7 +2,10 @@ import path from 'node:path' import { defineConfig } from 'vitest/config' -import { autoImportsPlugin } from '@cedarjs/testing/vitest' +import { + autoImportsPlugin, + trackDbImportsPlugin, +} from '@cedarjs/testing/vitest' import { cedarjsDirectoryNamedImportPlugin, getEnvVarDefinitions, @@ -10,12 +13,9 @@ import { export default defineConfig({ plugins: [ - // I hate to do this, but it's the pragmatic solution for now. The underlying - // issue is a version missmatch between Vite in CedarJS and Vite in the plugin - // TODO: How does this affect projects using this? - // _@ts-expect-error - version mismatch autoImportsPlugin(), cedarjsDirectoryNamedImportPlugin(), + trackDbImportsPlugin(), ], define: getEnvVarDefinitions(), ssr: { diff --git a/yarn.lock b/yarn.lock index 9020e2f390..2566a7939d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3508,6 +3508,8 @@ __metadata: unplugin-auto-import: "npm:19.3.0" vitest: "npm:3.2.4" whatwg-fetch: "npm:3.6.20" + peerDependencies: + vitest: 3.2.4 languageName: unknown linkType: soft From 767007965faa165dee2f60e446d0d5caec846332 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 17:10:07 +0200 Subject: [PATCH 072/222] vitest config plugin --- packages/testing/src/vitest/index.ts | 1 + .../vite-plugin-cedar-api-vitest-config.ts | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 8d0f405326..e8cbe224b2 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -4,3 +4,4 @@ export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' export { autoImportsPlugin } from './vite-plugin-auto-import.js' export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' +export { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts new file mode 100644 index 0000000000..932e58c8fd --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -0,0 +1,33 @@ +import path from 'node:path' + +import type { Plugin } from 'vite' + +import { getEnvVarDefinitions } from '@cedarjs/vite' + +export function cedarApiVitestConfigPlugin(): Plugin { + return { + name: 'cedar-vitest-plugin', + config: () => { + return { + define: getEnvVarDefinitions(), + ssr: { + noExternal: ['@cedarjs/testing'], + }, + resolve: { + alias: { + src: path.resolve(import.meta.dirname, './src'), + }, + }, + test: { + // fileParallelism: false, + // fileParallelism doesn't work with vitest projects (which is what we're + // using in the root vitest.config.ts). As a workaround we set poolOptions + // instead, which also shouldn't work, but was suggested by Vitest team + // member AriPerkkio (Hiroshi's answer didn't work). + // https://github.com/vitest-dev/vitest/discussions/7416 + poolOptions: { forks: { singleFork: true } }, + }, + } + }, + } +} From 0db29ed70e1e4574f9f815e0507c723a4202aa6d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 19:57:03 +0200 Subject: [PATCH 073/222] move globalSetup into custom vitest env --- packages/cli/src/commands/testHandler.js | 1 - .../testing/src/vitest/CedarApiVitestEnv.ts | 59 +++++++++++++++++++ .../__tests__/directUrlHelpers.test.ts | 0 .../src/{api => vitest}/directUrlHelpers.ts | 0 .../vite-plugin-cedar-api-vitest-config.ts | 55 +++++++++++------ 5 files changed, 95 insertions(+), 20 deletions(-) create mode 100644 packages/testing/src/vitest/CedarApiVitestEnv.ts rename packages/testing/src/{api => vitest}/__tests__/directUrlHelpers.test.ts (100%) rename packages/testing/src/{api => vitest}/directUrlHelpers.ts (100%) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 1a7e6a5aa2..626f6f97f4 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -66,7 +66,6 @@ export const handler = async ({ ...forwardJestFlags, collectCoverage ? '--collectCoverage' : null, '--passWithNoTests', - '--changed', ].filter((flagOrValue) => flagOrValue !== null) // Filter out nulls, not booleans because user may have passed a --something false flag // If the user wants to watch, set the proper watch flag based on what kind of repo this is diff --git a/packages/testing/src/vitest/CedarApiVitestEnv.ts b/packages/testing/src/vitest/CedarApiVitestEnv.ts new file mode 100644 index 0000000000..3f9614cf4a --- /dev/null +++ b/packages/testing/src/vitest/CedarApiVitestEnv.ts @@ -0,0 +1,59 @@ +import { getSchema } from '@prisma/internals' +import 'dotenv-defaults/config' +import execa from 'execa' +import type { Environment } from 'vitest/environments' + +import { getPaths } from '@cedarjs/project-config' + +import { getDefaultDb, checkAndReplaceDirectUrl } from './directUrlHelpers.js' + +const CedarApiVitestEnvironment: Environment = { + name: 'cedar-api', + transformMode: 'ssr', + + async setup() { + if (process.env.SKIP_DB_PUSH === '1') { + return { + teardown() {}, + } + } + + const cedarPaths = getPaths() + const defaultDb = getDefaultDb(cedarPaths.base) + + process.env.DATABASE_URL = process.env.TEST_DATABASE_URL || defaultDb + + // NOTE: This is a workaround to get the directUrl from the schema + // Instead of using the schema, we can use the config file + // const prismaConfig = await getConfig(rwjsPaths.api.dbSchema) + // and then check for the prismaConfig.datasources[0].directUrl + const prismaSchema = (await getSchema(cedarPaths.api.dbSchema)).toString() + + const directUrlEnvVar = checkAndReplaceDirectUrl(prismaSchema, defaultDb) + + const command = + process.env.TEST_DATABASE_STRATEGY === 'reset' + ? ['prisma', 'migrate', 'reset', '--force', '--skip-seed'] + : ['prisma', 'db', 'push', '--force-reset', '--accept-data-loss'] + + const directUrlDefinition = directUrlEnvVar + ? { [directUrlEnvVar]: process.env[directUrlEnvVar] } + : {} + + execa.sync(`yarn rw`, command, { + cwd: cedarPaths.api.base, + stdio: 'inherit', + shell: true, + env: { + DATABASE_URL: process.env.DATABASE_URL, + ...directUrlDefinition, + }, + }) + + return { + teardown() {}, + } + }, +} + +export default CedarApiVitestEnvironment diff --git a/packages/testing/src/api/__tests__/directUrlHelpers.test.ts b/packages/testing/src/vitest/__tests__/directUrlHelpers.test.ts similarity index 100% rename from packages/testing/src/api/__tests__/directUrlHelpers.test.ts rename to packages/testing/src/vitest/__tests__/directUrlHelpers.test.ts diff --git a/packages/testing/src/api/directUrlHelpers.ts b/packages/testing/src/vitest/directUrlHelpers.ts similarity index 100% rename from packages/testing/src/api/directUrlHelpers.ts rename to packages/testing/src/vitest/directUrlHelpers.ts diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index 932e58c8fd..cf55c4806a 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -2,32 +2,49 @@ import path from 'node:path' import type { Plugin } from 'vite' +import { getPaths } from '@cedarjs/project-config' import { getEnvVarDefinitions } from '@cedarjs/vite' export function cedarApiVitestConfigPlugin(): Plugin { return { name: 'cedar-vitest-plugin', - config: () => { - return { - define: getEnvVarDefinitions(), - ssr: { - noExternal: ['@cedarjs/testing'], - }, - resolve: { - alias: { - src: path.resolve(import.meta.dirname, './src'), - }, - }, - test: { - // fileParallelism: false, - // fileParallelism doesn't work with vitest projects (which is what we're - // using in the root vitest.config.ts). As a workaround we set poolOptions - // instead, which also shouldn't work, but was suggested by Vitest team - // member AriPerkkio (Hiroshi's answer didn't work). - // https://github.com/vitest-dev/vitest/discussions/7416 - poolOptions: { forks: { singleFork: true } }, + config: (userConfig) => { + userConfig.define = { + ...getEnvVarDefinitions(), + ...userConfig.define, + } + + let userNoExternal = userConfig.ssr?.noExternal ?? [] + if (!Array.isArray(userNoExternal)) { + userNoExternal = [] + } + + // This is best-effort. If the user has noExternal configured with + // something that's not an array this might not behave as they expect. + // But this has to be good enough for now. + userConfig.ssr = { + ...userConfig.ssr, + noExternal: ['@cedarjs/testing', ...userNoExternal], + } + + // TODO: Merge with userConfig.resolve.alias + userConfig.resolve = { + alias: { + src: getPaths().api.src, }, } + + userConfig.test = { + environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), + // fileParallelism: false, + // fileParallelism doesn't work with vitest projects (which is what we're + // using in the root vitest.config.ts). As a workaround we set poolOptions + // instead, which also shouldn't work, but was suggested by Vitest team + // member AriPerkkio (Hiroshi's answer didn't work). + // https://github.com/vitest-dev/vitest/discussions/7416 + poolOptions: { forks: { singleFork: true } }, + ...userConfig.test, + } }, } } From 7c5fe15413a1e0b019d76b814552450ec6a5312c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Tue, 22 Jul 2025 22:38:04 +0200 Subject: [PATCH 074/222] merge fw vitest setup files with user's --- .../src/vitest/vite-plugin-cedar-api-vitest-config.ts | 9 +++++++++ packages/testing/src/vitest/vitest-api.setup.ts | 3 ++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index cf55c4806a..34cb8b8911 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -34,6 +34,11 @@ export function cedarApiVitestConfigPlugin(): Plugin { }, } + const existingSetupFiles = userConfig?.test?.setupFiles ?? [] + const existingSetupFilesArray = Array.isArray(existingSetupFiles) + ? existingSetupFiles + : [existingSetupFiles] + userConfig.test = { environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), // fileParallelism: false, @@ -44,6 +49,10 @@ export function cedarApiVitestConfigPlugin(): Plugin { // https://github.com/vitest-dev/vitest/discussions/7416 poolOptions: { forks: { singleFork: true } }, ...userConfig.test, + setupFiles: [ + path.join(import.meta.dirname, 'vitest-api.setup.js'), + ...existingSetupFilesArray, + ], } }, } diff --git a/packages/testing/src/vitest/vitest-api.setup.ts b/packages/testing/src/vitest/vitest-api.setup.ts index e83a2202a4..ce43f6217e 100644 --- a/packages/testing/src/vitest/vitest-api.setup.ts +++ b/packages/testing/src/vitest/vitest-api.setup.ts @@ -304,7 +304,8 @@ async function loadScenarios(testPath: string, scenarioName: string) { scenario = allScenarios[scenarioName] } else { throw new Error( - `UndefinedScenario: There is no scenario named "${scenarioName}" in ${testFilePath}.{js,ts}`, + `UndefinedScenario: There is no scenario named "${scenarioName}" in ` + + `${testFilePath}.{js,ts}`, ) } } From 691f3f9c011d8d7a6a9c248cf7dde5bac630ad29 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 00:24:25 +0200 Subject: [PATCH 075/222] sync vitest-api.setup.ts with project --- packages/testing/src/api/globalSetup.ts | 49 ----------------- .../testing/src/vitest/vitest-api.setup.ts | 52 ++++--------------- 2 files changed, 11 insertions(+), 90 deletions(-) delete mode 100644 packages/testing/src/api/globalSetup.ts diff --git a/packages/testing/src/api/globalSetup.ts b/packages/testing/src/api/globalSetup.ts deleted file mode 100644 index 0277b31261..0000000000 --- a/packages/testing/src/api/globalSetup.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { getSchema } from '@prisma/internals' -import 'dotenv-defaults/config' -import execa from 'execa' - -import { getPaths } from '@cedarjs/project-config' - -import { getDefaultDb, checkAndReplaceDirectUrl } from './directUrlHelpers.js' - -const cedarPaths = getPaths() - -// TODO: ⛔️ Figure out how often this runs and compare it with the setup -// function in CedarApiVitestEnv. Can we maybe just use one of the two? Or do we -// need both? -export async function setup() { - if (process.env.SKIP_DB_PUSH === '1') { - return - } - - const defaultDb = getDefaultDb(cedarPaths.base) - - process.env.DATABASE_URL = process.env.TEST_DATABASE_URL || defaultDb - - // NOTE: This is a workaround to get the directUrl from the schema - // Instead of using the schema, we can use the config file - // const prismaConfig = await getConfig(rwjsPaths.api.dbSchema) - // and then check for the prismaConfig.datasources[0].directUrl - const prismaSchema = (await getSchema(cedarPaths.api.dbSchema)).toString() - - const directUrlEnvVar = checkAndReplaceDirectUrl(prismaSchema, defaultDb) - - const command = - process.env.TEST_DATABASE_STRATEGY === 'reset' - ? ['prisma', 'migrate', 'reset', '--force', '--skip-seed'] - : ['prisma', 'db', 'push', '--force-reset', '--accept-data-loss'] - - const directUrlDefinition = directUrlEnvVar - ? { [directUrlEnvVar]: process.env[directUrlEnvVar] } - : {} - - execa.sync(`yarn rw`, command, { - cwd: cedarPaths.api.base, - stdio: 'inherit', - shell: true, - env: { - DATABASE_URL: process.env.DATABASE_URL, - ...directUrlDefinition, - }, - }) -} diff --git a/packages/testing/src/vitest/vitest-api.setup.ts b/packages/testing/src/vitest/vitest-api.setup.ts index ce43f6217e..e392f9506e 100644 --- a/packages/testing/src/vitest/vitest-api.setup.ts +++ b/packages/testing/src/vitest/vitest-api.setup.ts @@ -66,9 +66,11 @@ globalThis.mockCurrentUser = (currentUser: Record | null) => { declare global { // eslint-disable-next-line no-var var defineScenario: DefineScenario + // eslint-disable-next-line no-var + var __cedarjs_db_imported__: string } -global.defineScenario = defineScenario +globalThis.defineScenario = defineScenario const cedarPaths = getPaths() @@ -117,14 +119,10 @@ function buildScenario(itFunc: It) { const scenarioData = await seedScenario(scenario) try { - console.log(`Running test ${testName} with scenario ${scenarioName}`) const result = await testFunc(scenarioData) return result } finally { - console.log( - `Cleaning up after test ${testName} with scenario ${scenarioName}`, - ) // Make sure to cleanup, even if test fails await teardown() } @@ -174,7 +172,7 @@ function buildDescribeScenario(describeFunc: Describe) { } async function configureTeardown() { - if (!wasDbUsed()) { + if (!wasDbImported()) { return } @@ -188,8 +186,6 @@ async function configureTeardown() { return m.dbName || m.name }) - console.log('schema models', schemaModels) - // check if pre-defined delete order already exists and if so, use it to start if (fs.existsSync(TEARDOWN_CACHE_PATH)) { teardownOrder = JSON.parse(fs.readFileSync(TEARDOWN_CACHE_PATH).toString()) @@ -201,8 +197,6 @@ async function configureTeardown() { teardownOrder = schemaModels } - console.log('teardown order', teardownOrder) - // keep a copy of the original order to compare against originalTeardownOrder = deepCopy(teardownOrder) } @@ -212,7 +206,7 @@ beforeAll(async () => { }) async function teardown() { - if (!wasDbUsed()) { + if (!wasDbImported()) { return } @@ -222,7 +216,6 @@ async function teardown() { for (const modelName of teardownOrder) { try { const query = `DELETE FROM ${quoteStyle}${modelName}${quoteStyle}` - console.log(`Deleting ${modelName} using query: ${query}`) await projectDb.$executeRawUnsafe(query) } catch (e) { console.error('teardown error\n', e) @@ -324,31 +317,8 @@ async function loadScenarios(testPath: string, scenarioName: string) { * Just disconnecting db in jest-preset is not enough, because the Prisma client * is created in a different context. */ -const wasDbUsed = () => { - // This code doesn't work in an ESM environment. There is no require, and even - // if I could create one, there's no require cache to look for db imports in. - // - // Some alternative solutions I could try implementing: - // - Use vitest to mock the db lib import, wrapping it with a simple proxy - // that records usage - // - Use a vite plugin to replace the db lib import with a mock that records - // usage - // - // For now I'm just returning `true`, which isn't as effective as it could be - // but should at least unblock me for now - // - // try { - // const libDbPath = require.resolve(`${cedarPaths.api.lib}/db`) - // return Object.keys(require.cache).some((module) => { - // return module === libDbPath - // }) - // } catch { - // // If db wasn't resolved, no point trying to perform db resets - // return false - // } - - // TODO: ⛔️ Be smarter about this - return true +const wasDbImported = () => { + return Boolean(globalThis.__cedarjs_db_imported__) } let quoteStyle: string @@ -431,7 +401,7 @@ declare global { var describeScenario: DescribeScenario } -global.scenario = buildScenario(it) -global.scenario.only = buildScenario(it.only) -global.describeScenario = buildDescribeScenario(describe) -global.describeScenario.only = buildDescribeScenario(describe.only) +globalThis.scenario = buildScenario(it) +globalThis.scenario.only = buildScenario(it.only) +globalThis.describeScenario = buildDescribeScenario(describe) +globalThis.describeScenario.only = buildDescribeScenario(describe.only) From ee3163f59f925beeea460337c7f908dbb540748e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 00:29:47 +0200 Subject: [PATCH 076/222] updated vitest config --- .../testing/src/vitest/vitest-api.config.ts | 28 ++----------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts index 7afbc80a7f..60ce11f321 100644 --- a/packages/testing/src/vitest/vitest-api.config.ts +++ b/packages/testing/src/vitest/vitest-api.config.ts @@ -1,42 +1,20 @@ -import path from 'node:path' - import { defineConfig } from 'vitest/config' import { autoImportsPlugin, trackDbImportsPlugin, + cedarApiVitestConfigPlugin, } from '@cedarjs/testing/vitest' -import { - cedarjsDirectoryNamedImportPlugin, - getEnvVarDefinitions, -} from '@cedarjs/vite' +import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' export default defineConfig({ plugins: [ + cedarApiVitestConfigPlugin(), autoImportsPlugin(), cedarjsDirectoryNamedImportPlugin(), trackDbImportsPlugin(), ], - define: getEnvVarDefinitions(), - ssr: { - noExternal: ['@cedarjs/testing'], - }, - resolve: { - alias: { - src: path.resolve(__dirname, './src'), - }, - }, test: { - environment: './src/CedarApiVitestEnv.ts', - // fileParallelism: false, - // fileParallelism doesn't work with vitest projects (which is what we're - // using in the root vitest.config.ts). As a workaround we set poolOptions - // instead, which also shouldn't work, but was suggested by Vitest team - // member AriPerkkio (Hiroshi's answer didn't work). - // https://github.com/vitest-dev/vitest/discussions/7416 - poolOptions: { forks: { singleFork: true } }, globals: true, - globalSetup: ['./src/globalSetup.ts'], - setupFiles: ['./vitest.setup.ts'], }, }) From 33967ad63b2f8acb7beed93b805756e17bb18cac Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:12:27 +0200 Subject: [PATCH 077/222] use mergeConfig() for vitest api side config --- .../vite-plugin-cedar-api-vitest-config.ts | 104 +++++++++++------- 1 file changed, 65 insertions(+), 39 deletions(-) diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index 34cb8b8911..62904bacf9 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -1,5 +1,6 @@ import path from 'node:path' +import { mergeConfig } from 'vite' import type { Plugin } from 'vite' import { getPaths } from '@cedarjs/project-config' @@ -9,51 +10,76 @@ export function cedarApiVitestConfigPlugin(): Plugin { return { name: 'cedar-vitest-plugin', config: (userConfig) => { - userConfig.define = { - ...getEnvVarDefinitions(), - ...userConfig.define, - } + // userConfig.define = { + // ...getEnvVarDefinitions(), + // ...userConfig.define, + // } - let userNoExternal = userConfig.ssr?.noExternal ?? [] - if (!Array.isArray(userNoExternal)) { - userNoExternal = [] - } + // let userNoExternal = userConfig.ssr?.noExternal ?? [] + // if (!Array.isArray(userNoExternal)) { + // userNoExternal = [] + // } - // This is best-effort. If the user has noExternal configured with - // something that's not an array this might not behave as they expect. - // But this has to be good enough for now. - userConfig.ssr = { - ...userConfig.ssr, - noExternal: ['@cedarjs/testing', ...userNoExternal], - } + // // This is best-effort. If the user has noExternal configured with + // // something that's not an array this might not behave as they expect. + // // But this has to be good enough for now. + // userConfig.ssr = { + // ...userConfig.ssr, + // noExternal: ['@cedarjs/testing', ...userNoExternal], + // } + + // // TODO: Merge with userConfig.resolve.alias + // userConfig.resolve = { + // alias: { + // src: getPaths().api.src, + // }, + // } + + // let userSetupFiles = userConfig?.test?.setupFiles ?? [] + // if (!Array.isArray(userSetupFiles)) { + // userSetupFiles = [userSetupFiles] + // } - // TODO: Merge with userConfig.resolve.alias - userConfig.resolve = { - alias: { - src: getPaths().api.src, + // userConfig.test = { + // environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), + // // fileParallelism: false, + // // fileParallelism doesn't work with vitest projects (which is what we're + // // using in the root vitest.config.ts). As a workaround we set poolOptions + // // instead, which also shouldn't work, but was suggested by Vitest team + // // member AriPerkkio (Hiroshi's answer didn't work). + // // https://github.com/vitest-dev/vitest/discussions/7416 + // poolOptions: { forks: { singleFork: true } }, + // ...userConfig.test, + // setupFiles: [ + // path.join(import.meta.dirname, 'vitest-api.setup.js'), + // ...userSetupFiles, + // ], + // } + + const cedarConfig = { + define: getEnvVarDefinitions(), + ssr: { + noExternal: ['@cedarjs/testing'], + }, + resolve: { + alias: { + src: getPaths().api.src, + }, + }, + test: { + environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), + // fileParallelism: false, + // fileParallelism doesn't work with vitest projects (which is what we're + // using in the root vitest.config.ts). As a workaround we set poolOptions + // instead, which also shouldn't work, but was suggested by Vitest team + // member AriPerkkio (Hiroshi's answer didn't work). + // https://github.com/vitest-dev/vitest/discussions/7416 + poolOptions: { forks: { singleFork: true } }, + setupFiles: [path.join(import.meta.dirname, 'vitest-api.setup.js')], }, } - const existingSetupFiles = userConfig?.test?.setupFiles ?? [] - const existingSetupFilesArray = Array.isArray(existingSetupFiles) - ? existingSetupFiles - : [existingSetupFiles] - - userConfig.test = { - environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), - // fileParallelism: false, - // fileParallelism doesn't work with vitest projects (which is what we're - // using in the root vitest.config.ts). As a workaround we set poolOptions - // instead, which also shouldn't work, but was suggested by Vitest team - // member AriPerkkio (Hiroshi's answer didn't work). - // https://github.com/vitest-dev/vitest/discussions/7416 - poolOptions: { forks: { singleFork: true } }, - ...userConfig.test, - setupFiles: [ - path.join(import.meta.dirname, 'vitest-api.setup.js'), - ...existingSetupFilesArray, - ], - } + return mergeConfig(cedarConfig, userConfig) }, } } From d01477a6e3493aa673ca8a8cbab9a188cd6ee29d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:18:21 +0200 Subject: [PATCH 078/222] remove commendted code --- .../vite-plugin-cedar-api-vitest-config.ts | 46 ------------------- 1 file changed, 46 deletions(-) diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index 62904bacf9..be9252c36f 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -10,52 +10,6 @@ export function cedarApiVitestConfigPlugin(): Plugin { return { name: 'cedar-vitest-plugin', config: (userConfig) => { - // userConfig.define = { - // ...getEnvVarDefinitions(), - // ...userConfig.define, - // } - - // let userNoExternal = userConfig.ssr?.noExternal ?? [] - // if (!Array.isArray(userNoExternal)) { - // userNoExternal = [] - // } - - // // This is best-effort. If the user has noExternal configured with - // // something that's not an array this might not behave as they expect. - // // But this has to be good enough for now. - // userConfig.ssr = { - // ...userConfig.ssr, - // noExternal: ['@cedarjs/testing', ...userNoExternal], - // } - - // // TODO: Merge with userConfig.resolve.alias - // userConfig.resolve = { - // alias: { - // src: getPaths().api.src, - // }, - // } - - // let userSetupFiles = userConfig?.test?.setupFiles ?? [] - // if (!Array.isArray(userSetupFiles)) { - // userSetupFiles = [userSetupFiles] - // } - - // userConfig.test = { - // environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), - // // fileParallelism: false, - // // fileParallelism doesn't work with vitest projects (which is what we're - // // using in the root vitest.config.ts). As a workaround we set poolOptions - // // instead, which also shouldn't work, but was suggested by Vitest team - // // member AriPerkkio (Hiroshi's answer didn't work). - // // https://github.com/vitest-dev/vitest/discussions/7416 - // poolOptions: { forks: { singleFork: true } }, - // ...userConfig.test, - // setupFiles: [ - // path.join(import.meta.dirname, 'vitest-api.setup.js'), - // ...userSetupFiles, - // ], - // } - const cedarConfig = { define: getEnvVarDefinitions(), ssr: { From 5abf8fc9e8272c1623360f1a4a74da87601e1a0e Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:24:43 +0200 Subject: [PATCH 079/222] .js import and source format --- packages/testing/src/vitest/CedarApiVitestEnv.ts | 2 +- .../src/vitest/vite-plugin-cedar-api-vitest-config.ts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/testing/src/vitest/CedarApiVitestEnv.ts b/packages/testing/src/vitest/CedarApiVitestEnv.ts index 3f9614cf4a..46443c8260 100644 --- a/packages/testing/src/vitest/CedarApiVitestEnv.ts +++ b/packages/testing/src/vitest/CedarApiVitestEnv.ts @@ -1,5 +1,5 @@ import { getSchema } from '@prisma/internals' -import 'dotenv-defaults/config' +import 'dotenv-defaults/config.js' import execa from 'execa' import type { Environment } from 'vitest/environments' diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index be9252c36f..210447ada5 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -23,10 +23,10 @@ export function cedarApiVitestConfigPlugin(): Plugin { test: { environment: path.join(import.meta.dirname, 'CedarApiVitestEnv.js'), // fileParallelism: false, - // fileParallelism doesn't work with vitest projects (which is what we're - // using in the root vitest.config.ts). As a workaround we set poolOptions - // instead, which also shouldn't work, but was suggested by Vitest team - // member AriPerkkio (Hiroshi's answer didn't work). + // fileParallelism doesn't work with vitest projects (which is what + // we're using in the root vitest.config.ts). As a workaround we set + // poolOptions instead, which also shouldn't work, but was suggested + // by Vitest team member AriPerkkio (Hiroshi's answer didn't work). // https://github.com/vitest-dev/vitest/discussions/7416 poolOptions: { forks: { singleFork: true } }, setupFiles: [path.join(import.meta.dirname, 'vitest-api.setup.js')], From 80dcbc61bacf3e802fd9ae57def7aee438eb94e0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:52:39 +0200 Subject: [PATCH 080/222] Let vitest handle merging --- .../src/vitest/vite-plugin-cedar-api-vitest-config.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts index 210447ada5..8e5b492598 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts @@ -1,6 +1,5 @@ import path from 'node:path' -import { mergeConfig } from 'vite' import type { Plugin } from 'vite' import { getPaths } from '@cedarjs/project-config' @@ -9,8 +8,8 @@ import { getEnvVarDefinitions } from '@cedarjs/vite' export function cedarApiVitestConfigPlugin(): Plugin { return { name: 'cedar-vitest-plugin', - config: (userConfig) => { - const cedarConfig = { + config: () => { + return { define: getEnvVarDefinitions(), ssr: { noExternal: ['@cedarjs/testing'], @@ -32,8 +31,6 @@ export function cedarApiVitestConfigPlugin(): Plugin { setupFiles: [path.join(import.meta.dirname, 'vitest-api.setup.js')], }, } - - return mergeConfig(cedarConfig, userConfig) }, } } From 100ba35e16d964827bbd570319ed1ec0499106b5 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:53:08 +0200 Subject: [PATCH 081/222] vitest preset --- packages/testing/src/vitest/index.ts | 1 + .../src/vitest/vite-plugin-cedar-api-vitest.ts | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index e8cbe224b2..0d9168413e 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -5,3 +5,4 @@ export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-impor export { autoImportsPlugin } from './vite-plugin-auto-import.js' export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' export { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' +export { cedarApiVitestPreset } from './vite-plugin-cedar-api-vitest.js' diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts new file mode 100644 index 0000000000..7388c6f9f8 --- /dev/null +++ b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts @@ -0,0 +1,14 @@ +import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' + +import { autoImportsPlugin } from './vite-plugin-auto-import.js' +import { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' +import { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' + +export function cedarApiVitestPreset() { + return [ + cedarApiVitestConfigPlugin(), + autoImportsPlugin(), + cedarjsDirectoryNamedImportPlugin(), + trackDbImportsPlugin(), + ] +} From 4bf5bc585e80b5e8cbf81b421b555d046957f0cc Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 07:54:00 +0200 Subject: [PATCH 082/222] rename to -preset.ts --- packages/testing/src/vitest/index.ts | 2 +- ...dar-api-vitest.ts => vite-plugin-cedar-api-vitest-preset.ts} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename packages/testing/src/vitest/{vite-plugin-cedar-api-vitest.ts => vite-plugin-cedar-api-vitest-preset.ts} (100%) diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/vitest/index.ts index 0d9168413e..d97d62bc49 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/vitest/index.ts @@ -5,4 +5,4 @@ export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-impor export { autoImportsPlugin } from './vite-plugin-auto-import.js' export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' export { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' -export { cedarApiVitestPreset } from './vite-plugin-cedar-api-vitest.js' +export { cedarApiVitestPreset } from './vite-plugin-cedar-api-vitest-preset.js' diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts b/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-preset.ts similarity index 100% rename from packages/testing/src/vitest/vite-plugin-cedar-api-vitest.ts rename to packages/testing/src/vitest/vite-plugin-cedar-api-vitest-preset.ts From 5b609888dff7599af7654819fa6039d38679f32a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 08:07:33 +0200 Subject: [PATCH 083/222] export vitest preset from /api --- packages/testing/src/api/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/testing/src/api/index.ts b/packages/testing/src/api/index.ts index da494f0ac2..affcd89d5c 100644 --- a/packages/testing/src/api/index.ts +++ b/packages/testing/src/api/index.ts @@ -1,3 +1,4 @@ export * from './apiFunction.js' export * from './scenario.js' export * from './directive.js' +export { cedarApiVitestPreset } from '../vitest/vite-plugin-cedar-api-vitest-preset.js' From cea0cb4d00a3d25b690c33e1fa798c5aff31d7ed Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 09:55:20 +0200 Subject: [PATCH 084/222] refactor testing module --- packages/testing/package.json | 12 ++-- packages/testing/src/api/index.ts | 2 +- .../src/{ => api}/vitest/CedarApiVitestEnv.ts | 0 .../vitest/__tests__/directUrlHelpers.test.ts | 0 .../src/{ => api}/vitest/directUrlHelpers.ts | 0 packages/testing/src/api/vitest/index.ts | 3 + .../src/api/vitest/vite-plugin-auto-import.ts | 36 +++++++++++ .../vite-plugin-cedar-vitest-api-config.ts} | 2 +- .../vite-plugin-cedar-vitest-api-preset.ts} | 6 +- .../vitest/vite-plugin-track-db-imports.ts | 0 .../src/api/vitest/vitest-api.config.ts | 10 ++++ .../src/{ => api}/vitest/vitest-api.setup.ts | 0 .../src/vitest/vite-plugin-auto-import.ts | 60 ------------------- .../testing/src/vitest/vitest-api.config.ts | 20 ------- .../testing/src/{ => web}/vitest/index.ts | 3 - .../mockProvidersRelativeRoutesPathsPlugin.ts | 0 .../vitest/mockProvidersRoutesPlugin.ts | 0 .../src/web/vitest/vite-plugin-auto-import.ts | 25 ++++++++ ...-plugin-cedarjs-router-import-transform.ts | 0 ...ite-plugin-create-auth-import-transform.ts | 0 packages/vite/src/index.ts | 2 +- 21 files changed, 88 insertions(+), 93 deletions(-) rename packages/testing/src/{ => api}/vitest/CedarApiVitestEnv.ts (100%) rename packages/testing/src/{ => api}/vitest/__tests__/directUrlHelpers.test.ts (100%) rename packages/testing/src/{ => api}/vitest/directUrlHelpers.ts (100%) create mode 100644 packages/testing/src/api/vitest/index.ts create mode 100644 packages/testing/src/api/vitest/vite-plugin-auto-import.ts rename packages/testing/src/{vitest/vite-plugin-cedar-api-vitest-config.ts => api/vitest/vite-plugin-cedar-vitest-api-config.ts} (95%) rename packages/testing/src/{vitest/vite-plugin-cedar-api-vitest-preset.ts => api/vitest/vite-plugin-cedar-vitest-api-preset.ts} (66%) rename packages/testing/src/{ => api}/vitest/vite-plugin-track-db-imports.ts (100%) create mode 100644 packages/testing/src/api/vitest/vitest-api.config.ts rename packages/testing/src/{ => api}/vitest/vitest-api.setup.ts (100%) delete mode 100644 packages/testing/src/vitest/vite-plugin-auto-import.ts delete mode 100644 packages/testing/src/vitest/vitest-api.config.ts rename packages/testing/src/{ => web}/vitest/index.ts (64%) rename packages/testing/src/{ => web}/vitest/mockProvidersRelativeRoutesPathsPlugin.ts (100%) rename packages/testing/src/{ => web}/vitest/mockProvidersRoutesPlugin.ts (100%) create mode 100644 packages/testing/src/web/vitest/vite-plugin-auto-import.ts rename packages/testing/src/{ => web}/vitest/vite-plugin-cedarjs-router-import-transform.ts (100%) rename packages/testing/src/{ => web}/vitest/vite-plugin-create-auth-import-transform.ts (100%) diff --git a/packages/testing/package.json b/packages/testing/package.json index 920152097a..5a78af81f1 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -51,6 +51,10 @@ "default": "./dist/cjs/api/index.js" } }, + "./api/vitest": { + "types": "./dist/api/vitest/index.d.ts", + "default": "./dist/api/vitest/index.js" + }, "./cache": { "import": { "types": "./dist/cache/index.d.ts", @@ -61,10 +65,6 @@ "default": "./dist/cjs/cache/index.js" } }, - "./vitest": { - "types": "./dist/vitest/index.d.ts", - "default": "./dist/vitest/index.js" - }, "./web": { "import": { "types": "./dist/web/index.d.ts", @@ -85,6 +85,10 @@ "default": "./dist/cjs/web/MockRouter.js" } }, + "./web/vitest": { + "types": "./dist/web/vitest/index.d.ts", + "default": "./dist/web/vitest/index.js" + }, "./mockContext": { "// TODO": "./is the mockContext export still needed?", "import": { diff --git a/packages/testing/src/api/index.ts b/packages/testing/src/api/index.ts index affcd89d5c..168f67a7f9 100644 --- a/packages/testing/src/api/index.ts +++ b/packages/testing/src/api/index.ts @@ -1,4 +1,4 @@ export * from './apiFunction.js' export * from './scenario.js' export * from './directive.js' -export { cedarApiVitestPreset } from '../vitest/vite-plugin-cedar-api-vitest-preset.js' +export { cedarVitestPreset } from './vitest/vite-plugin-cedar-vitest-api-preset.js' diff --git a/packages/testing/src/vitest/CedarApiVitestEnv.ts b/packages/testing/src/api/vitest/CedarApiVitestEnv.ts similarity index 100% rename from packages/testing/src/vitest/CedarApiVitestEnv.ts rename to packages/testing/src/api/vitest/CedarApiVitestEnv.ts diff --git a/packages/testing/src/vitest/__tests__/directUrlHelpers.test.ts b/packages/testing/src/api/vitest/__tests__/directUrlHelpers.test.ts similarity index 100% rename from packages/testing/src/vitest/__tests__/directUrlHelpers.test.ts rename to packages/testing/src/api/vitest/__tests__/directUrlHelpers.test.ts diff --git a/packages/testing/src/vitest/directUrlHelpers.ts b/packages/testing/src/api/vitest/directUrlHelpers.ts similarity index 100% rename from packages/testing/src/vitest/directUrlHelpers.ts rename to packages/testing/src/api/vitest/directUrlHelpers.ts diff --git a/packages/testing/src/api/vitest/index.ts b/packages/testing/src/api/vitest/index.ts new file mode 100644 index 0000000000..95a6a2063f --- /dev/null +++ b/packages/testing/src/api/vitest/index.ts @@ -0,0 +1,3 @@ +export { autoImportsPlugin } from './vite-plugin-auto-import.js' +export { cedarVitestApiConfigPlugin } from './vite-plugin-cedar-vitest-api-config.js' +export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' diff --git a/packages/testing/src/api/vitest/vite-plugin-auto-import.ts b/packages/testing/src/api/vitest/vite-plugin-auto-import.ts new file mode 100644 index 0000000000..191dcfd572 --- /dev/null +++ b/packages/testing/src/api/vitest/vite-plugin-auto-import.ts @@ -0,0 +1,36 @@ +import autoImport from 'unplugin-auto-import/vite' + +export function autoImportsPlugin() { + return autoImport({ + // targets to transform + include: [ + /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + ], + + // global imports to register + imports: [ + // import { mockContext, mockHttpEvent, mockSignedWebhook } from '@cedarjs/testing/api'; + { + '@cedarjs/testing/api': [ + 'mockContext', + 'mockHttpEvent', + 'mockSignedWebhook', + ], + }, + // import gql from 'graphql-tag' + { + 'graphql-tag': ['gql'], + }, + // import { context } from '@cedarjs/context' + { + '@cedarjs/context': ['context'], + }, + ], + + // We provide our mocking types elsewhere and so don't need this plugin to + // generate them. + // TODO: Maybe we should have it at least generate the types for the gql + // import? (Or do we already provide that some other way?) + dts: false, + }) +} diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts similarity index 95% rename from packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts rename to packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts index 8e5b492598..ccd1d9237d 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-config.ts +++ b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts @@ -5,7 +5,7 @@ import type { Plugin } from 'vite' import { getPaths } from '@cedarjs/project-config' import { getEnvVarDefinitions } from '@cedarjs/vite' -export function cedarApiVitestConfigPlugin(): Plugin { +export function cedarVitestApiConfigPlugin(): Plugin { return { name: 'cedar-vitest-plugin', config: () => { diff --git a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-preset.ts b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts similarity index 66% rename from packages/testing/src/vitest/vite-plugin-cedar-api-vitest-preset.ts rename to packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts index 7388c6f9f8..bb235acd62 100644 --- a/packages/testing/src/vitest/vite-plugin-cedar-api-vitest-preset.ts +++ b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts @@ -1,12 +1,12 @@ import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' import { autoImportsPlugin } from './vite-plugin-auto-import.js' -import { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' +import { cedarVitestApiConfigPlugin } from './vite-plugin-cedar-vitest-api-config.js' import { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' -export function cedarApiVitestPreset() { +export function cedarVitestPreset() { return [ - cedarApiVitestConfigPlugin(), + cedarVitestApiConfigPlugin(), autoImportsPlugin(), cedarjsDirectoryNamedImportPlugin(), trackDbImportsPlugin(), diff --git a/packages/testing/src/vitest/vite-plugin-track-db-imports.ts b/packages/testing/src/api/vitest/vite-plugin-track-db-imports.ts similarity index 100% rename from packages/testing/src/vitest/vite-plugin-track-db-imports.ts rename to packages/testing/src/api/vitest/vite-plugin-track-db-imports.ts diff --git a/packages/testing/src/api/vitest/vitest-api.config.ts b/packages/testing/src/api/vitest/vitest-api.config.ts new file mode 100644 index 0000000000..81ad38d527 --- /dev/null +++ b/packages/testing/src/api/vitest/vitest-api.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +import { cedarVitestPreset } from '@cedarjs/testing/api' + +export default defineConfig({ + plugins: [cedarVitestPreset()], + test: { + globals: true, + }, +}) diff --git a/packages/testing/src/vitest/vitest-api.setup.ts b/packages/testing/src/api/vitest/vitest-api.setup.ts similarity index 100% rename from packages/testing/src/vitest/vitest-api.setup.ts rename to packages/testing/src/api/vitest/vitest-api.setup.ts diff --git a/packages/testing/src/vitest/vite-plugin-auto-import.ts b/packages/testing/src/vitest/vite-plugin-auto-import.ts deleted file mode 100644 index bcc42f66d6..0000000000 --- a/packages/testing/src/vitest/vite-plugin-auto-import.ts +++ /dev/null @@ -1,60 +0,0 @@ -import autoImport from 'unplugin-auto-import/vite' - -export function autoImportsPlugin() { - return [ - autoImport({ - // targets to transform - include: [ - /web\/src\/.*\.[tj]sx?$/, // .ts, .tsx, .js, .jsx - ], - - // global imports to register - imports: [ - { - '@cedarjs/testing/web': [ - 'mockGraphQLQuery', - 'mockGraphQLMutation', - 'mockCurrentUser', - ], - }, - ], - - // We provide our mocking types elsewhere and so don't need this plugin to - // generate them. - dts: false, - }), - - autoImport({ - // targets to transform - include: [ - /api\/src\/.*\.[tj]sx?$/, // .ts, .tsx, .js, .jsx - ], - - // global imports to register - imports: [ - // import { mockContext, mockHttpEvent, mockSignedWebhook } from '@cedarjs/testing/api'; - { - '@cedarjs/testing/api': [ - 'mockContext', - 'mockHttpEvent', - 'mockSignedWebhook', - ], - }, - // import gql from 'graphql-tag' - { - 'graphql-tag': ['gql'], - }, - // import { context } from '@cedarjs/context' - { - '@cedarjs/context': ['context'], - }, - ], - - // We provide our mocking types elsewhere and so don't need this plugin to - // generate them. - // TODO: Maybe we should have it at least generate the types for the gql - // import? (Or do we already provide that some other way?) - dts: false, - }), - ] -} diff --git a/packages/testing/src/vitest/vitest-api.config.ts b/packages/testing/src/vitest/vitest-api.config.ts deleted file mode 100644 index 60ce11f321..0000000000 --- a/packages/testing/src/vitest/vitest-api.config.ts +++ /dev/null @@ -1,20 +0,0 @@ -import { defineConfig } from 'vitest/config' - -import { - autoImportsPlugin, - trackDbImportsPlugin, - cedarApiVitestConfigPlugin, -} from '@cedarjs/testing/vitest' -import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' - -export default defineConfig({ - plugins: [ - cedarApiVitestConfigPlugin(), - autoImportsPlugin(), - cedarjsDirectoryNamedImportPlugin(), - trackDbImportsPlugin(), - ], - test: { - globals: true, - }, -}) diff --git a/packages/testing/src/vitest/index.ts b/packages/testing/src/web/vitest/index.ts similarity index 64% rename from packages/testing/src/vitest/index.ts rename to packages/testing/src/web/vitest/index.ts index d97d62bc49..f78cd74491 100644 --- a/packages/testing/src/vitest/index.ts +++ b/packages/testing/src/web/vitest/index.ts @@ -3,6 +3,3 @@ export { mockProvidersRelativeRoutesPathsPlugin } from './mockProvidersRelativeR export { cedarJsRouterImportTransformPlugin } from './vite-plugin-cedarjs-router-import-transform.js' export { createAuthImportTransformPlugin } from './vite-plugin-create-auth-import-transform.js' export { autoImportsPlugin } from './vite-plugin-auto-import.js' -export { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' -export { cedarApiVitestConfigPlugin } from './vite-plugin-cedar-api-vitest-config.js' -export { cedarApiVitestPreset } from './vite-plugin-cedar-api-vitest-preset.js' diff --git a/packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts b/packages/testing/src/web/vitest/mockProvidersRelativeRoutesPathsPlugin.ts similarity index 100% rename from packages/testing/src/vitest/mockProvidersRelativeRoutesPathsPlugin.ts rename to packages/testing/src/web/vitest/mockProvidersRelativeRoutesPathsPlugin.ts diff --git a/packages/testing/src/vitest/mockProvidersRoutesPlugin.ts b/packages/testing/src/web/vitest/mockProvidersRoutesPlugin.ts similarity index 100% rename from packages/testing/src/vitest/mockProvidersRoutesPlugin.ts rename to packages/testing/src/web/vitest/mockProvidersRoutesPlugin.ts diff --git a/packages/testing/src/web/vitest/vite-plugin-auto-import.ts b/packages/testing/src/web/vitest/vite-plugin-auto-import.ts new file mode 100644 index 0000000000..36797a1929 --- /dev/null +++ b/packages/testing/src/web/vitest/vite-plugin-auto-import.ts @@ -0,0 +1,25 @@ +import autoImport from 'unplugin-auto-import/vite' + +export function autoImportsPlugin() { + return autoImport({ + // targets to transform + include: [ + /\.[tj]sx?$/, // .ts, .tsx, .js, .jsx + ], + + // global imports to register + imports: [ + { + '@cedarjs/testing/web': [ + 'mockGraphQLQuery', + 'mockGraphQLMutation', + 'mockCurrentUser', + ], + }, + ], + + // We provide our mocking types elsewhere and so don't need this plugin to + // generate them. + dts: false, + }) +} diff --git a/packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts b/packages/testing/src/web/vitest/vite-plugin-cedarjs-router-import-transform.ts similarity index 100% rename from packages/testing/src/vitest/vite-plugin-cedarjs-router-import-transform.ts rename to packages/testing/src/web/vitest/vite-plugin-cedarjs-router-import-transform.ts diff --git a/packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts b/packages/testing/src/web/vitest/vite-plugin-create-auth-import-transform.ts similarity index 100% rename from packages/testing/src/vitest/vite-plugin-create-auth-import-transform.ts rename to packages/testing/src/web/vitest/vite-plugin-create-auth-import-transform.ts diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 6542e07ca3..2888bacc40 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -9,7 +9,7 @@ import { // mockProvidersRelativeRoutesPathsPlugin, cedarJsRouterImportTransformPlugin, createAuthImportTransformPlugin, -} from '@cedarjs/testing/vitest' +} from '@cedarjs/testing/web/vitest' import { cedarCellTransform } from './plugins/vite-plugin-cedar-cell.js' import { cedarEntryInjectionPlugin } from './plugins/vite-plugin-cedar-entry-injection.js' From e8021fd98b27129effdbdaea790974edebdda475 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 10:40:39 +0200 Subject: [PATCH 085/222] update templates --- __fixtures__/test-project/api/vitest.config.ts | 12 ++---------- __fixtures__/test-project/package.json | 5 +++-- .../templates/js/api/vitest.config.js | 12 ++---------- packages/create-cedar-app/templates/js/package.json | 3 ++- .../templates/ts/api/vitest.config.ts | 12 ++---------- packages/create-cedar-app/templates/ts/package.json | 3 ++- 6 files changed, 13 insertions(+), 34 deletions(-) diff --git a/__fixtures__/test-project/api/vitest.config.ts b/__fixtures__/test-project/api/vitest.config.ts index bac6839c94..81ad38d527 100644 --- a/__fixtures__/test-project/api/vitest.config.ts +++ b/__fixtures__/test-project/api/vitest.config.ts @@ -1,18 +1,10 @@ -import path from 'node:path' - import { defineConfig } from 'vitest/config' -import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' +import { cedarVitestPreset } from '@cedarjs/testing/api' export default defineConfig({ - plugins: [cedarjsDirectoryNamedImportPlugin()], - resolve: { - alias: { - src: path.resolve(__dirname, './src'), - }, - }, + plugins: [cedarVitestPreset()], test: { - fileParallelism: false, globals: true, }, }) diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index 1295a98810..070faaa6b1 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -26,6 +26,7 @@ "packageManager": "yarn@4.9.2", "resolutions": { "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", - "react-is": "19.0.0-rc-f2df5694-20240916" + "react-is": "19.0.0-rc-f2df5694-20240916", + "vite": "5.4.16" } -} \ No newline at end of file +} diff --git a/packages/create-cedar-app/templates/js/api/vitest.config.js b/packages/create-cedar-app/templates/js/api/vitest.config.js index bac6839c94..81ad38d527 100644 --- a/packages/create-cedar-app/templates/js/api/vitest.config.js +++ b/packages/create-cedar-app/templates/js/api/vitest.config.js @@ -1,18 +1,10 @@ -import path from 'node:path' - import { defineConfig } from 'vitest/config' -import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' +import { cedarVitestPreset } from '@cedarjs/testing/api' export default defineConfig({ - plugins: [cedarjsDirectoryNamedImportPlugin()], - resolve: { - alias: { - src: path.resolve(__dirname, './src'), - }, - }, + plugins: [cedarVitestPreset()], test: { - fileParallelism: false, globals: true, }, }) diff --git a/packages/create-cedar-app/templates/js/package.json b/packages/create-cedar-app/templates/js/package.json index cb67591a47..c890d69f0e 100644 --- a/packages/create-cedar-app/templates/js/package.json +++ b/packages/create-cedar-app/templates/js/package.json @@ -25,6 +25,7 @@ "packageManager": "yarn@4.9.2", "resolutions": { "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", - "react-is": "19.0.0-rc-f2df5694-20240916" + "react-is": "19.0.0-rc-f2df5694-20240916", + "vite": "5.4.16" } } diff --git a/packages/create-cedar-app/templates/ts/api/vitest.config.ts b/packages/create-cedar-app/templates/ts/api/vitest.config.ts index bac6839c94..81ad38d527 100644 --- a/packages/create-cedar-app/templates/ts/api/vitest.config.ts +++ b/packages/create-cedar-app/templates/ts/api/vitest.config.ts @@ -1,18 +1,10 @@ -import path from 'node:path' - import { defineConfig } from 'vitest/config' -import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' +import { cedarVitestPreset } from '@cedarjs/testing/api' export default defineConfig({ - plugins: [cedarjsDirectoryNamedImportPlugin()], - resolve: { - alias: { - src: path.resolve(__dirname, './src'), - }, - }, + plugins: [cedarVitestPreset()], test: { - fileParallelism: false, globals: true, }, }) diff --git a/packages/create-cedar-app/templates/ts/package.json b/packages/create-cedar-app/templates/ts/package.json index cb67591a47..c890d69f0e 100644 --- a/packages/create-cedar-app/templates/ts/package.json +++ b/packages/create-cedar-app/templates/ts/package.json @@ -25,6 +25,7 @@ "packageManager": "yarn@4.9.2", "resolutions": { "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", - "react-is": "19.0.0-rc-f2df5694-20240916" + "react-is": "19.0.0-rc-f2df5694-20240916", + "vite": "5.4.16" } } From bd44a4dfd10e84a4b2799ae420b2183d2400146c Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 11:10:19 +0200 Subject: [PATCH 086/222] move public vitest preset export to vite package --- __fixtures__/test-project/api/vitest.config.ts | 2 +- .../templates/js/api/vitest.config.js | 2 +- .../templates/ts/api/vitest.config.ts | 2 +- packages/testing/src/api/index.ts | 1 - .../vitest/vite-plugin-cedar-vitest-api-preset.ts | 14 -------------- .../testing/src/api/vitest/vitest-api.config.ts | 2 +- packages/vite/package.json | 4 ++++ .../src/api/vite-plugin-cedar-vitest-api-preset.ts | 14 ++++++++++++++ 8 files changed, 22 insertions(+), 19 deletions(-) delete mode 100644 packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts create mode 100644 packages/vite/src/api/vite-plugin-cedar-vitest-api-preset.ts diff --git a/__fixtures__/test-project/api/vitest.config.ts b/__fixtures__/test-project/api/vitest.config.ts index 81ad38d527..f3de348f75 100644 --- a/__fixtures__/test-project/api/vitest.config.ts +++ b/__fixtures__/test-project/api/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config' -import { cedarVitestPreset } from '@cedarjs/testing/api' +import { cedarVitestPreset } from '@cedarjs/vite/api' export default defineConfig({ plugins: [cedarVitestPreset()], diff --git a/packages/create-cedar-app/templates/js/api/vitest.config.js b/packages/create-cedar-app/templates/js/api/vitest.config.js index 81ad38d527..f3de348f75 100644 --- a/packages/create-cedar-app/templates/js/api/vitest.config.js +++ b/packages/create-cedar-app/templates/js/api/vitest.config.js @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config' -import { cedarVitestPreset } from '@cedarjs/testing/api' +import { cedarVitestPreset } from '@cedarjs/vite/api' export default defineConfig({ plugins: [cedarVitestPreset()], diff --git a/packages/create-cedar-app/templates/ts/api/vitest.config.ts b/packages/create-cedar-app/templates/ts/api/vitest.config.ts index 81ad38d527..f3de348f75 100644 --- a/packages/create-cedar-app/templates/ts/api/vitest.config.ts +++ b/packages/create-cedar-app/templates/ts/api/vitest.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config' -import { cedarVitestPreset } from '@cedarjs/testing/api' +import { cedarVitestPreset } from '@cedarjs/vite/api' export default defineConfig({ plugins: [cedarVitestPreset()], diff --git a/packages/testing/src/api/index.ts b/packages/testing/src/api/index.ts index 168f67a7f9..da494f0ac2 100644 --- a/packages/testing/src/api/index.ts +++ b/packages/testing/src/api/index.ts @@ -1,4 +1,3 @@ export * from './apiFunction.js' export * from './scenario.js' export * from './directive.js' -export { cedarVitestPreset } from './vitest/vite-plugin-cedar-vitest-api-preset.js' diff --git a/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts deleted file mode 100644 index bb235acd62..0000000000 --- a/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-preset.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { cedarjsDirectoryNamedImportPlugin } from '@cedarjs/vite' - -import { autoImportsPlugin } from './vite-plugin-auto-import.js' -import { cedarVitestApiConfigPlugin } from './vite-plugin-cedar-vitest-api-config.js' -import { trackDbImportsPlugin } from './vite-plugin-track-db-imports.js' - -export function cedarVitestPreset() { - return [ - cedarVitestApiConfigPlugin(), - autoImportsPlugin(), - cedarjsDirectoryNamedImportPlugin(), - trackDbImportsPlugin(), - ] -} diff --git a/packages/testing/src/api/vitest/vitest-api.config.ts b/packages/testing/src/api/vitest/vitest-api.config.ts index 81ad38d527..f3de348f75 100644 --- a/packages/testing/src/api/vitest/vitest-api.config.ts +++ b/packages/testing/src/api/vitest/vitest-api.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'vitest/config' -import { cedarVitestPreset } from '@cedarjs/testing/api' +import { cedarVitestPreset } from '@cedarjs/vite/api' export default defineConfig({ plugins: [cedarVitestPreset()], diff --git a/packages/vite/package.json b/packages/vite/package.json index 06af755fb9..d42420c9e3 100644 --- a/packages/vite/package.json +++ b/packages/vite/package.json @@ -21,6 +21,10 @@ "default": "./dist/cjs/index.js" } }, + "./api": { + "types": "./dist/api/vite-plugin-cedar-vitest-api-preset.d.ts", + "default": "./dist/api/vite-plugin-cedar-vitest-api-preset.js" + }, "./client": { "require": "./dist/cjs/client.js", "import": "./dist/client.js" diff --git a/packages/vite/src/api/vite-plugin-cedar-vitest-api-preset.ts b/packages/vite/src/api/vite-plugin-cedar-vitest-api-preset.ts new file mode 100644 index 0000000000..a2aa39d082 --- /dev/null +++ b/packages/vite/src/api/vite-plugin-cedar-vitest-api-preset.ts @@ -0,0 +1,14 @@ +import { autoImportsPlugin } from '@cedarjs/testing/api/vitest' +import { cedarVitestApiConfigPlugin } from '@cedarjs/testing/api/vitest' +import { trackDbImportsPlugin } from '@cedarjs/testing/api/vitest' + +import { cedarjsDirectoryNamedImportPlugin } from '../plugins/vite-plugin-cedarjs-directory-named-import.js' + +export function cedarVitestPreset() { + return [ + cedarVitestApiConfigPlugin(), + autoImportsPlugin(), + cedarjsDirectoryNamedImportPlugin(), + trackDbImportsPlugin(), + ] +} From a712d18a36b77726dd04b3b22689dc735695de36 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 11:42:47 +0200 Subject: [PATCH 087/222] copy envVarDefinitions to testing package to avoid circular deps --- .../src/api/vitest/envVarDefinitions.ts | 55 +++++++++++++++++++ .../vite-plugin-cedar-vitest-api-config.ts | 3 +- .../src/api/vitest/vitest-api.config.ts | 10 ---- 3 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 packages/testing/src/api/vitest/envVarDefinitions.ts delete mode 100644 packages/testing/src/api/vitest/vitest-api.config.ts diff --git a/packages/testing/src/api/vitest/envVarDefinitions.ts b/packages/testing/src/api/vitest/envVarDefinitions.ts new file mode 100644 index 0000000000..8d2c54b455 --- /dev/null +++ b/packages/testing/src/api/vitest/envVarDefinitions.ts @@ -0,0 +1,55 @@ +import path from 'node:path' + +import { getConfig, getPaths } from '@cedarjs/project-config' + +export function getEnvVarDefinitions() { + const rwConfig = getConfig() + const rwPaths = getPaths() + + return { + RWJS_ENV: { + RWJS_API_GRAPHQL_URL: + rwConfig.web.apiGraphQLUrl ?? rwConfig.web.apiUrl + '/graphql', + RWJS_API_URL: rwConfig.web.apiUrl, + __REDWOOD__APP_TITLE: rwConfig.web.title || path.basename(rwPaths.base), + RWJS_EXP_STREAMING_SSR: rwConfig.experimental.streamingSsr?.enabled, + RWJS_EXP_RSC: rwConfig.experimental?.rsc?.enabled, + }, + RWJS_DEBUG_ENV: { + RWJS_SRC_ROOT: rwPaths.web.src, + REDWOOD_ENV_EDITOR: JSON.stringify(process.env.REDWOOD_ENV_EDITOR), + }, + // Vite can automatically expose environment variables, but we + // disable that in `buildFeServer.ts` by setting `envFile: false` + // because we want to use our own logic for loading .env, + // .env.defaults, etc + // The two object spreads below will expose all environment + // variables listed in redwood.toml and all environment variables + // prefixed with REDWOOD_ENV_ + ...Object.fromEntries( + rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ + // TODO: Figure out if/why we need to disable eslint here, when we + // didn't have to before, when this code was in index.ts + // Re-enable if possible + // eslint-disable-next-line + [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], + // TODO: Figure out if/why we need to disable eslint here, when we + // didn't have to before, when this code was in index.ts + // Re-enable if possible + // eslint-disable-next-line + [`process.env.${envName}`, JSON.stringify(process.env[envName])], + ]), + ), + ...Object.entries(process.env).reduce>( + (acc, [key, value]) => { + if (key.startsWith('REDWOOD_ENV_')) { + acc[`import.meta.env.${key}`] = JSON.stringify(value) + acc[`process.env.${key}`] = JSON.stringify(value) + } + + return acc + }, + {}, + ), + } +} diff --git a/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts index ccd1d9237d..e69ebbe2c8 100644 --- a/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts +++ b/packages/testing/src/api/vitest/vite-plugin-cedar-vitest-api-config.ts @@ -3,7 +3,8 @@ import path from 'node:path' import type { Plugin } from 'vite' import { getPaths } from '@cedarjs/project-config' -import { getEnvVarDefinitions } from '@cedarjs/vite' + +import { getEnvVarDefinitions } from './envVarDefinitions.js' export function cedarVitestApiConfigPlugin(): Plugin { return { diff --git a/packages/testing/src/api/vitest/vitest-api.config.ts b/packages/testing/src/api/vitest/vitest-api.config.ts deleted file mode 100644 index f3de348f75..0000000000 --- a/packages/testing/src/api/vitest/vitest-api.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'vitest/config' - -import { cedarVitestPreset } from '@cedarjs/vite/api' - -export default defineConfig({ - plugins: [cedarVitestPreset()], - test: { - globals: true, - }, -}) From f215bda1a5bbe2a43078346bca38446d769b9812 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 11:43:33 +0200 Subject: [PATCH 088/222] copy envVarDefinitions to testing package to avoid circular deps --- packages/testing/src/api/vitest/envVarDefinitions.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/testing/src/api/vitest/envVarDefinitions.ts b/packages/testing/src/api/vitest/envVarDefinitions.ts index 8d2c54b455..18fe4a8e82 100644 --- a/packages/testing/src/api/vitest/envVarDefinitions.ts +++ b/packages/testing/src/api/vitest/envVarDefinitions.ts @@ -28,15 +28,7 @@ export function getEnvVarDefinitions() { // prefixed with REDWOOD_ENV_ ...Object.fromEntries( rwConfig.web.includeEnvironmentVariables.flatMap((envName) => [ - // TODO: Figure out if/why we need to disable eslint here, when we - // didn't have to before, when this code was in index.ts - // Re-enable if possible - // eslint-disable-next-line [`import.meta.env.${envName}`, JSON.stringify(process.env[envName])], - // TODO: Figure out if/why we need to disable eslint here, when we - // didn't have to before, when this code was in index.ts - // Re-enable if possible - // eslint-disable-next-line [`process.env.${envName}`, JSON.stringify(process.env[envName])], ]), ), From 21cb7cec845fcd59c45c992d3c4b6ab5f9f3048a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 12:23:06 +0200 Subject: [PATCH 089/222] add todo for env var config --- packages/testing/src/api/vitest/envVarDefinitions.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/packages/testing/src/api/vitest/envVarDefinitions.ts b/packages/testing/src/api/vitest/envVarDefinitions.ts index 18fe4a8e82..07d6288ac5 100644 --- a/packages/testing/src/api/vitest/envVarDefinitions.ts +++ b/packages/testing/src/api/vitest/envVarDefinitions.ts @@ -1,3 +1,12 @@ +// This file is copy/pasted from @cedarjs/vite to avoid circular dependencies. +// I'd much, much, rather have a single source of truth for this, so I want to +// try to come up with a better solution that ideally doesn't involve creating +// yet another new package just for this single file. +// Maybe I can do something fun with `import.meta.resolve` to make this a pure +// runtime circular dependency, and not a build time one. That would help. +// +// TODO: ⛔️ Try putting this file in @cedarjs/project-config. See if that +// works. import path from 'node:path' import { getConfig, getPaths } from '@cedarjs/project-config' From c00c6a3446170ba6b6e6630800aa8acb3e983d43 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 12:23:30 +0200 Subject: [PATCH 090/222] update test project fixture --- .../test-project/api/src/services/contacts/contacts.test.ts | 4 ++-- .../test-project/api/src/services/posts/posts.test.ts | 4 ++-- __fixtures__/test-project/package.json | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts b/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts index bf017e4606..30c1b833b0 100644 --- a/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts +++ b/__fixtures__/test-project/api/src/services/contacts/contacts.test.ts @@ -6,8 +6,8 @@ import { createContact, updateContact, deleteContact, -} from './contacts' -import type { StandardScenario } from './contacts.scenarios' +} from './contacts.js' +import type { StandardScenario } from './contacts.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. diff --git a/__fixtures__/test-project/api/src/services/posts/posts.test.ts b/__fixtures__/test-project/api/src/services/posts/posts.test.ts index fc316574f5..b7c7572341 100644 --- a/__fixtures__/test-project/api/src/services/posts/posts.test.ts +++ b/__fixtures__/test-project/api/src/services/posts/posts.test.ts @@ -1,7 +1,7 @@ import type { Post } from '@prisma/client' -import { posts, post, createPost, updatePost, deletePost } from './posts' -import type { StandardScenario } from './posts.scenarios' +import { posts, post, createPost, updatePost, deletePost } from './posts.js' +import type { StandardScenario } from './posts.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index 070faaa6b1..a25bd5d72a 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -29,4 +29,4 @@ "react-is": "19.0.0-rc-f2df5694-20240916", "vite": "5.4.16" } -} +} \ No newline at end of file From a871cb64de2e584480821d8e8cf24423d7cdc211 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 13:05:11 +0200 Subject: [PATCH 091/222] fix tests --- .../cli/src/commands/__tests__/test.test.js | 28 ++++++++------- .../__tests__/__snapshots__/sdl.test.js.snap | 8 ++--- .../__snapshots__/service.test.js.snap | 24 ++++++------- packages/cli/src/commands/test.js | 36 ++++++++----------- packages/cli/src/commands/testHandler.js | 33 +++++++---------- 5 files changed, 58 insertions(+), 71 deletions(-) diff --git a/packages/cli/src/commands/__tests__/test.test.js b/packages/cli/src/commands/__tests__/test.test.js index 63110b0828..d4eba523d9 100644 --- a/packages/cli/src/commands/__tests__/test.test.js +++ b/packages/cli/src/commands/__tests__/test.test.js @@ -21,16 +21,16 @@ vi.mock('@cedarjs/structure', () => { } }) -// Before rw tests run, api/ and web/ `jest.config.js` is confirmed via existsSync() -vi.mock('fs-extra', async (importOriginal) => { - const originalFsExtra = await importOriginal() - return { - default: { - ...originalFsExtra, - existsSync: () => true, - }, - } -}) +// Before rw tests run, api/ and web/ `vitest.config.ts` is confirmed via existsSync() +// vi.mock('fs-extra', async (importOriginal) => { +// const originalFsExtra = await importOriginal() +// return { +// default: { +// ...originalFsExtra, +// existsSync: () => true, +// }, +// } +// }) afterEach(() => { vi.clearAllMocks() @@ -96,7 +96,7 @@ test('Does not create db when calling test with just web', async () => { expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') }) -test('Passes filter param to jest command if passed', async () => { +test('Passes filter param to vitest command if passed', async () => { await handler({ filter: ['web', 'bazinga'], }) @@ -105,12 +105,13 @@ test('Passes filter param to jest command if passed', async () => { expect(execa.mock.results[0].value.params).toContain('bazinga') }) -test('Passes other flags to jest', async () => { +test('Passes other flags to vitest', async () => { await handler({ u: true, debug: true, json: true, collectCoverage: true, + watch: true, }) expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') @@ -118,9 +119,10 @@ test('Passes other flags to jest', async () => { expect(execa.mock.results[0].value.params).toContain('--debug') expect(execa.mock.results[0].value.params).toContain('--json') expect(execa.mock.results[0].value.params).toContain('--collectCoverage') + expect(execa.mock.results[0].value.params).toContain('--watch') }) -test('Passes values of other flags to jest', async () => { +test('Passes values of other flags to vitest', async () => { await handler({ bazinga: false, hello: 'world', diff --git a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap index fce1cd9b04..0d357f9c8f 100644 --- a/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap +++ b/packages/cli/src/commands/generate/sdl/__tests__/__snapshots__/sdl.test.js.snap @@ -54,7 +54,7 @@ exports[`handler > can be called with PascalCase model name 2`] = ` exports[`handler > can be called with PascalCase model name 3`] = ` { - "fileContent": "import { users, user, createUser, updateUser, deleteUser } from './users' + "fileContent": "import { users, user, createUser, updateUser, deleteUser } from './users.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -202,7 +202,7 @@ exports[`handler > can be called with PascalCase model name 7`] = ` createCustomData, updateCustomData, deleteCustomData, -} from './customDatums' +} from './customDatums.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -348,7 +348,7 @@ exports[`handler > can be called with camelCase model name 2`] = ` exports[`handler > can be called with camelCase model name 3`] = ` { - "fileContent": "import { users, user, createUser, updateUser, deleteUser } from './users' + "fileContent": "import { users, user, createUser, updateUser, deleteUser } from './users.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -496,7 +496,7 @@ exports[`handler > can be called with camelCase model name 7`] = ` createCustomData, updateCustomData, deleteCustomData, -} from './customDatums' +} from './customDatums.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. diff --git a/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap b/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap index b202ef638b..01359af02d 100644 --- a/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap +++ b/packages/cli/src/commands/generate/service/__tests__/__snapshots__/service.test.js.snap @@ -22,7 +22,7 @@ export const UserProfile = { `; exports[`in javascript mode > creates a multi word service test file 1`] = ` -"import { userProfiles } from './userProfiles' +"import { userProfiles } from './userProfiles.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -47,7 +47,7 @@ exports[`in javascript mode > creates a multi word service test file with crud a createTransaction, updateTransaction, deleteTransaction, -} from './transactions' +} from './transactions.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -107,7 +107,7 @@ exports[`in javascript mode > creates a multi word service test file with multip createScalarType, updateScalarType, deleteScalarType, -} from './scalarTypes' +} from './scalarTypes.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -330,7 +330,7 @@ exports[`in javascript mode > creates a single word service scenario file 1`] = `; exports[`in javascript mode > creates a single word service test file 1`] = ` -"import { users, user, createUser, updateUser, deleteUser } from './users' +"import { users, user, createUser, updateUser, deleteUser } from './users.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -408,8 +408,8 @@ export const UserProfile: UserProfileRelationResolvers = { exports[`in typescript mode > creates a multi word service test file 1`] = ` "import type { UserProfile } from '@prisma/client' -import { userProfiles } from './userProfiles' -import type { StandardScenario } from './userProfiles.scenarios' +import { userProfiles } from './userProfiles.js' +import type { StandardScenario } from './userProfiles.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -436,8 +436,8 @@ import { createTransaction, updateTransaction, deleteTransaction, -} from './transactions' -import type { StandardScenario } from './transactions.scenarios' +} from './transactions.js' +import type { StandardScenario } from './transactions.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -502,8 +502,8 @@ import { createScalarType, updateScalarType, deleteScalarType, -} from './scalarTypes' -import type { StandardScenario } from './scalarTypes.scenarios' +} from './scalarTypes.js' +import type { StandardScenario } from './scalarTypes.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -755,8 +755,8 @@ export type StandardScenario = ScenarioData exports[`in typescript mode > creates a single word service test file 1`] = ` "import type { User } from '@prisma/client' -import { users, user, createUser, updateUser, deleteUser } from './users' -import type { StandardScenario } from './users.scenarios' +import { users, user, createUser, updateUser, deleteUser } from './users.js' +import type { StandardScenario } from './users.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. diff --git a/packages/cli/src/commands/test.js b/packages/cli/src/commands/test.js index 285aa640c6..d242232bb9 100644 --- a/packages/cli/src/commands/test.js +++ b/packages/cli/src/commands/test.js @@ -4,41 +4,33 @@ import c from '../lib/colors.js' import { sides } from '../lib/project.js' export const command = 'test [filter..]' -export const description = 'Run Jest tests. Defaults to watch mode' +export const description = 'Run Vitest tests. Defaults to watch mode' export const builder = (yargs) => { + const cliDocsLink = terminalLink( + 'CedarJS CLI Reference', + 'https://cedarjs.com/docs/cli-commands#test', + ) + const vitestTip = c.tip('yarn vitest --help') + yargs - .strict(false) // so that we can forward arguments to jest + .strict(false) // so that we can forward arguments to vitest .positional('filter', { default: sides(), description: - 'Which side(s) to test, and/or a regular expression to match against your test files to filter by', + 'Which side(s) to test, and/or a regular expression to match against ' + + 'your test files to filter by', type: 'array', }) - .option('watch', { - describe: - 'Run tests related to changed files based on hg/git. Specify the name or path to a file to focus on a specific set of tests', - type: 'boolean', - default: true, - }) - .option('collect-coverage', { - describe: - 'Show test coverage summary and output info to coverage directory', - type: 'boolean', - default: false, - }) .option('db-push', { describe: - "Syncs the test database with your Prisma schema without requiring a migration. It creates a test database if it doesn't already exist.", + 'Syncs the test database with your Prisma schema without requiring a ' + + "migration. It creates a test database if it doesn't already exist.", type: 'boolean', default: true, }) .epilogue( - `For all available flags, run jest cli directly ${c.tip( - 'yarn jest --help', - )}\n\nAlso see the ${terminalLink( - 'Redwood CLI Reference', - 'https://redwoodjs.com/docs/cli-commands#test', - )}\n`, + `For all available flags, run vitest cli directly ${vitestTip}\n\n` + + `Also see the ${cliDocsLink}\n`, ) } diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 626f6f97f4..63df94d298 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -9,29 +9,17 @@ import * as project from '../lib/project.js' export const handler = async ({ filter: filterParams = [], - watch = true, - collectCoverage = false, dbPush = true, ...others }) => { recordTelemetryAttributes({ command: 'test', - watch, - collectCoverage, dbPush, }) + let watch = true const rwjsPaths = getPaths() const forwardJestFlags = Object.keys(others).flatMap((flagName) => { - if ( - [ - 'collect-coverage', - 'db-push', - 'loadEnvFiles', - 'watch', - '$0', - '_', - ].includes(flagName) - ) { + if (['db-push', 'loadEnvFiles', '$0', '_'].includes(flagName)) { // filter out flags meant for the rw test command only return [] } else { @@ -39,6 +27,12 @@ export const handler = async ({ const flag = flagName.length > 1 ? `--${flagName}` : `-${flagName}` const flagValue = others[flagName] + if (flagName === 'watch') { + watch = flagValue === true + } else if (flagName === 'run' && flagValue) { + watch = false + } + if (Array.isArray(flagValue)) { // jest does not collapse flags e.g. --coverageReporters=html --coverageReporters=text // so we pass it on. Yargs collapses these flags into an array of values @@ -64,14 +58,13 @@ export const handler = async ({ const vitestArgs = [ ...jestFilterArgs, ...forwardJestFlags, - collectCoverage ? '--collectCoverage' : null, '--passWithNoTests', ].filter((flagOrValue) => flagOrValue !== null) // Filter out nulls, not booleans because user may have passed a --something false flag - // If the user wants to watch, set the proper watch flag based on what kind of repo this is - // because of https://github.com/facebook/create-react-app/issues/5210 - if (watch && !process.env.CI && !collectCoverage) { - vitestArgs.push('--watch') + if (process.env.CI) { + // Don't run in watch mode in CI + vitestArgs.push('--run') + watch = false } // if no sides declared with yargs, default to all sides @@ -98,7 +91,7 @@ export const handler = async ({ // https://github.com/facebook/jest/issues/5048 // TODO: Run vitest programmatically. See https://vitest.dev/advanced/api/ const runCommand = async () => { - await execa('yarn vitest run', vitestArgs, { + await execa('yarn vitest', vitestArgs, { cwd: rwjsPaths.base, shell: true, stdio: 'inherit', From 4250f3b1ec5d94365af720877a1e91175f831206 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 13:36:00 +0200 Subject: [PATCH 092/222] update fragment test project fixture --- .../fragment-test-project/.redwood/README.md | 26 +- .../.vscode/settings.json | 8 +- __fixtures__/fragment-test-project/README.md | 107 +----- .../migration.sql | 0 .../fragment-test-project/api/jest.config.js | 8 - .../fragment-test-project/api/package.json | 7 +- .../requireAuth/requireAuth.test.ts | 2 +- .../src/directives/requireAuth/requireAuth.ts | 2 +- .../src/directives/skipAuth/skipAuth.test.ts | 2 +- .../api/src/functions/auth.ts | 4 +- .../api/src/functions/graphql.ts | 6 +- .../api/src/graphql/produces.sdl.ts | 2 +- .../api/src/graphql/stalls.sdl.ts | 2 +- .../fragment-test-project/api/src/lib/auth.ts | 2 +- .../fragment-test-project/api/src/lib/db.ts | 17 +- .../src/services/contacts/contacts.test.ts | 8 +- .../api/src/services/contacts/contacts.ts | 2 +- .../contacts/describeContacts.scenarios.ts | 12 + .../contacts/describeContacts.test.ts | 57 ++++ .../api/src/services/posts/posts.scenarios.ts | 4 +- .../api/src/services/posts/posts.test.ts | 4 +- .../api/src/services/posts/posts.ts | 2 +- .../services/produces/produces.scenarios.ts | 29 +- .../src/services/produces/produces.test.ts | 74 ++-- .../api/src/services/produces/produces.ts | 38 +-- .../src/services/stalls/stalls.scenarios.ts | 13 +- .../api/src/services/stalls/stalls.test.ts | 68 ++-- .../api/src/services/stalls/stalls.ts | 38 +-- .../api/src/services/users/users.scenarios.ts | 4 +- .../api/src/services/users/users.ts | 2 +- .../fragment-test-project/api/tsconfig.json | 22 +- .../api/vitest.config.ts | 10 + .../{graphql.config.js => graphql.config.cjs} | 0 .../fragment-test-project/jest.config.js | 8 - .../fragment-test-project/package.json | 13 +- ...prettier.config.js => prettier.config.cjs} | 2 +- .../fragment-test-project/redwood.toml | 4 +- .../fragment-test-project/scripts/seed.ts | 14 +- .../scripts/tsconfig.json | 35 +- .../fragment-test-project/vitest.config.ts | 7 + .../{postcss.config.js => postcss.config.cjs} | 3 +- .../web/config/tailwind.config.cjs | 10 + .../web/config/tailwind.config.js | 8 - .../fragment-test-project/web/jest.config.js | 8 - .../fragment-test-project/web/package.json | 24 +- .../web/public/README.md | 12 +- .../web/public/favicon.png | Bin 1741 -> 2500 bytes .../fragment-test-project/web/src/App.tsx | 4 +- .../web/src/Redwood.stories.mdx | 5 +- .../fragment-test-project/web/src/Routes.tsx | 8 +- .../components/AuthorCell/AuthorCell.mock.ts | 1 + .../components/AuthorCell/AuthorCell.test.tsx | 4 +- .../BlogPostCell/BlogPostCell.mock.ts | 2 + .../BlogPostCell/BlogPostCell.test.tsx | 4 +- .../BlogPostsCell/BlogPostsCell.mock.ts | 6 + .../BlogPostsCell/BlogPostsCell.tsx | 36 +- .../ClassWithClassField.stories.tsx | 26 ++ .../ClassWithClassField.test.tsx | 14 + .../ClassWithClassField.tsx | 12 + .../components/Contact/Contact/Contact.tsx | 2 +- .../components/Contact/Contacts/Contacts.tsx | 2 +- .../Contact/ContactsCell/ContactsCell.tsx | 28 +- .../web/src/components/FruitInfo.tsx | 22 +- .../web/src/components/Post/Post/Post.tsx | 2 +- .../src/components/Post/PostCell/PostCell.tsx | 24 +- .../web/src/components/Post/Posts/Posts.tsx | 2 +- .../components/Post/PostsCell/PostsCell.tsx | 4 +- .../web/src/components/ProduceInfo.tsx | 14 +- .../web/src/components/StallInfo.tsx | 14 +- .../web/src/components/VegetableInfo.tsx | 22 +- .../WaterfallBlogPostCell.mock.ts | 2 + .../WaterfallBlogPostCell.test.tsx | 6 +- .../web/src/entry.client.tsx | 3 +- .../web/src/layouts/BlogLayout/BlogLayout.tsx | 27 +- .../layouts/ScaffoldLayout/ScaffoldLayout.tsx | 4 +- .../web/src/lib/formatters.test.tsx | 2 +- .../web/src/pages/AboutPage/AboutPage.tsx | 3 - .../src/pages/BlogPostPage/BlogPostPage.tsx | 3 +- .../src/pages/ContactUsPage/ContactUsPage.tsx | 39 ++- .../GroceriesPage/GroceriesPage.stories.tsx | 12 +- .../GroceriesPage/GroceriesPage.test.tsx | 16 +- .../src/pages/GroceriesPage/GroceriesPage.tsx | 8 +- .../web/src/pages/HomePage/HomePage.tsx | 3 - .../web/src/pages/LoginPage/LoginPage.tsx | 3 +- .../web/src/pages/ProfilePage/ProfilePage.tsx | 2 +- .../ResetPasswordPage/ResetPasswordPage.tsx | 8 +- .../web/src/pages/SignupPage/SignupPage.tsx | 3 +- .../src/pages/WaterfallPage/WaterfallPage.tsx | 1 - .../web/src/scaffold.css | 319 +++++++++++++----- .../fragment-test-project/web/tsconfig.json | 10 +- .../web/types/graphql.d.ts | 10 +- .../fragment-test-project/web/vite.config.ts | 16 +- .../fragment-test-project/web/vitest.setup.ts | 12 + 93 files changed, 870 insertions(+), 630 deletions(-) rename __fixtures__/fragment-test-project/api/db/migrations/{20240106111257_create_produce_stall => 20250723113422_create_produce_stall}/migration.sql (100%) delete mode 100644 __fixtures__/fragment-test-project/api/jest.config.js create mode 100644 __fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.scenarios.ts create mode 100644 __fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts create mode 100644 __fixtures__/fragment-test-project/api/vitest.config.ts rename __fixtures__/fragment-test-project/{graphql.config.js => graphql.config.cjs} (100%) delete mode 100644 __fixtures__/fragment-test-project/jest.config.js rename __fixtures__/fragment-test-project/{prettier.config.js => prettier.config.cjs} (87%) create mode 100644 __fixtures__/fragment-test-project/vitest.config.ts rename __fixtures__/fragment-test-project/web/config/{postcss.config.js => postcss.config.cjs} (77%) create mode 100644 __fixtures__/fragment-test-project/web/config/tailwind.config.cjs delete mode 100644 __fixtures__/fragment-test-project/web/config/tailwind.config.js delete mode 100644 __fixtures__/fragment-test-project/web/jest.config.js create mode 100644 __fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.stories.tsx create mode 100644 __fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.test.tsx create mode 100644 __fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.tsx create mode 100644 __fixtures__/fragment-test-project/web/vitest.setup.ts diff --git a/__fixtures__/fragment-test-project/.redwood/README.md b/__fixtures__/fragment-test-project/.redwood/README.md index f22b586a47..8a1bf5738b 100644 --- a/__fixtures__/fragment-test-project/.redwood/README.md +++ b/__fixtures__/fragment-test-project/.redwood/README.md @@ -14,24 +14,24 @@ You don't need to commit any other contents of this directory to your version co ### Files -| Name | Description | -| :---------------- | :------- | -| commandCache.json | This file contains mappings to assist the Redwood CLI in efficiently executing commands. | -| schema.graphql | This is the GraphQL schema which has been automatically generated from your Redwood project. | -| studio.db | The sqlite database used by the experimental `rw exp studio` feature. | +| Name | Description | +| :---------------- | :----------------------------------------------------------------------------------------------------------------- | +| commandCache.json | This file contains mappings to assist the Redwood CLI in efficiently executing commands. | +| schema.graphql | This is the GraphQL schema which has been automatically generated from your Redwood project. | | telemetry.txt | Contains a unique ID used for telemetry. This value is rotated every 24 hours to protect your project's anonymity. | -| test.db | The sqlite database used when running tests. | +| test.db | The sqlite database used when running tests. | ### Directories -| Name | Description | -| :---------- | :------- | -| locks | Stores temporary files that Redwood uses to keep track of the execution of async/background tasks between processes. | -| logs | Stores log files for background tasks such as update checking. | -| prebuild | Stores transpiled JavaScript that is generated as part of Redwood's build process. | +| Name | Description | +| :---------- | :----------------------------------------------------------------------------------------------------------------------------------------------- | +| locks | Stores temporary files that Redwood uses to keep track of the execution of async/background tasks between processes. | +| logs | Stores log files for background tasks such as update checking. | +| prebuild | Stores transpiled JavaScript that is generated as part of Redwood's build process. | | telemetry | Stores the recent telemetry that the Redwood CLI has generated. You may inspect these files to see everything Redwood is anonymously collecting. | -| types | Stores the results of type generation. | -| updateCheck | Stores a file which contains the results of checking for Redwood updates. | +| types | Stores the results of type generation. | +| updateCheck | Stores a file which contains the results of checking for Redwood updates. | +| studio | Used to store data for `rw studio` | We try to keep this README up to date but you may, from time to time, find other files or directories in this `.redwood` directory that have not yet been documented here. This is likely nothing to worry about but feel free to let us know and we'll update this list. diff --git a/__fixtures__/fragment-test-project/.vscode/settings.json b/__fixtures__/fragment-test-project/.vscode/settings.json index 6887d360eb..1d3afa8ae3 100644 --- a/__fixtures__/fragment-test-project/.vscode/settings.json +++ b/__fixtures__/fragment-test-project/.vscode/settings.json @@ -7,5 +7,11 @@ }, "[prisma]": { "editor.formatOnSave": true - } + }, + "tailwindCSS.classAttributes": [ + "class", + "className", + "activeClassName", + "errorClassName" + ] } diff --git a/__fixtures__/fragment-test-project/README.md b/__fixtures__/fragment-test-project/README.md index 60a38fe2c6..4487e7f63d 100644 --- a/__fixtures__/fragment-test-project/README.md +++ b/__fixtures__/fragment-test-project/README.md @@ -1,11 +1,6 @@ # README -Welcome to [RedwoodJS](https://redwoodjs.com)! - -> **Prerequisites** -> -> - Redwood requires [Node.js](https://nodejs.org/en/) (=20.x) and [Yarn](https://yarnpkg.com/) -> - Are you on Windows? For best results, follow our [Windows development setup](https://redwoodjs.com/docs/how-to/windows-development-setup) guide +Welcome to your new [CedarJS](https://cedarjs.com) project! Start by installing dependencies: @@ -20,103 +15,3 @@ yarn redwood dev ``` Your browser should automatically open to [http://localhost:8910](http://localhost:8910) where you'll see the Welcome Page, which links out to many great resources. - -> **The Redwood CLI** -> -> Congratulations on running your first Redwood CLI command! From dev to deploy, the CLI is with you the whole way. And there's quite a few commands at your disposal: -> -> ``` -> yarn redwood --help -> ``` -> -> For all the details, see the [CLI reference](https://redwoodjs.com/docs/cli-commands). - -## Prisma and the database - -Redwood wouldn't be a full-stack framework without a database. It all starts with the schema. Open the [`schema.prisma`](api/db/schema.prisma) file in `api/db` and replace the `UserExample` model with the following `Post` model: - -```prisma -model Post { - id Int @id @default(autoincrement()) - title String - body String - createdAt DateTime @default(now()) -} -``` - -Redwood uses [Prisma](https://www.prisma.io/), a next-gen Node.js and TypeScript ORM, to talk to the database. Prisma's schema offers a declarative way of defining your app's data models. And Prisma [Migrate](https://www.prisma.io/migrate) uses that schema to make database migrations hassle-free: - -``` -yarn rw prisma migrate dev - -# ... - -? Enter a name for the new migration: › create posts -``` - -> `rw` is short for `redwood` - -You'll be prompted for the name of your migration. `create posts` will do. - -Now let's generate everything we need to perform all the CRUD (Create, Retrieve, Update, Delete) actions on our `Post` model: - -``` -yarn redwood generate scaffold post -``` - -Navigate to [http://localhost:8910/posts/new](http://localhost:8910/posts/new), fill in the title and body, and click "Save". - -Did we just create a post in the database? Yup! With `yarn rw generate scaffold `, Redwood created all the pages, components, and services necessary to perform all CRUD actions on our posts table. - -## Frontend first with Storybook - -Don't know what your data models look like? That's more than ok—Redwood integrates Storybook so that you can work on design without worrying about data. Mockup, build, and verify your React components, even in complete isolation from the backend: - -``` -yarn rw storybook -``` - -Seeing "Couldn't find any stories"? That's because you need a `*.stories.{tsx,jsx}` file. The Redwood CLI makes getting one easy enough—try generating a [Cell](https://redwoodjs.com/docs/cells), Redwood's data-fetching abstraction: - -``` -yarn rw generate cell examplePosts -``` - -The Storybook server should hot reload and now you'll have four stories to work with. They'll probably look a little bland since there's no styling. See if the Redwood CLI's `setup ui` command has your favorite styling library: - -``` -yarn rw setup ui --help -``` - -## Testing with Jest - -It'd be hard to scale from side project to startup without a few tests. Redwood fully integrates Jest with both the front- and back-ends, and makes it easy to keep your whole app covered by generating test files with all your components and services: - -``` -yarn rw test -``` - -To make the integration even more seamless, Redwood augments Jest with database [scenarios](https://redwoodjs.com/docs/testing#scenarios) and [GraphQL mocking](https://redwoodjs.com/docs/testing#mocking-graphql-calls). - -## Ship it - -Redwood is designed for both serverless deploy targets like Netlify and Vercel and serverful deploy targets like Render and AWS: - -``` -yarn rw setup deploy --help -``` - -Don't go live without auth! Lock down your app with Redwood's built-in, database-backed authentication system ([dbAuth](https://redwoodjs.com/docs/authentication#self-hosted-auth-installation-and-setup)), or integrate with nearly a dozen third-party auth providers: - -``` -yarn rw setup auth --help -``` - -## Next Steps - -The best way to learn Redwood is by going through the comprehensive [tutorial](https://redwoodjs.com/docs/tutorial/foreword) and joining the community (via the [Discourse forum](https://community.redwoodjs.com) or the [Discord server](https://discord.gg/redwoodjs)). - -## Quick Links - -- Stay updated: read [Forum announcements](https://community.redwoodjs.com/c/announcements/5), follow us on [Twitter](https://twitter.com/redwoodjs), and subscribe to the [newsletter](https://redwoodjs.com/newsletter) -- [Learn how to contribute](https://redwoodjs.com/docs/contributing) diff --git a/__fixtures__/fragment-test-project/api/db/migrations/20240106111257_create_produce_stall/migration.sql b/__fixtures__/fragment-test-project/api/db/migrations/20250723113422_create_produce_stall/migration.sql similarity index 100% rename from __fixtures__/fragment-test-project/api/db/migrations/20240106111257_create_produce_stall/migration.sql rename to __fixtures__/fragment-test-project/api/db/migrations/20250723113422_create_produce_stall/migration.sql diff --git a/__fixtures__/fragment-test-project/api/jest.config.js b/__fixtures__/fragment-test-project/api/jest.config.js deleted file mode 100644 index 6646ef5637..0000000000 --- a/__fixtures__/fragment-test-project/api/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', - preset: '@cedarjs/testing/config/jest/api', -} - -module.exports = config diff --git a/__fixtures__/fragment-test-project/api/package.json b/__fixtures__/fragment-test-project/api/package.json index 88e7bdd885..f3b9e4e1cb 100644 --- a/__fixtures__/fragment-test-project/api/package.json +++ b/__fixtures__/fragment-test-project/api/package.json @@ -1,10 +1,11 @@ { "name": "api", + "type": "module", "version": "0.0.0", "private": true, "dependencies": { - "@cedarjs/api": "7.0.0", - "@cedarjs/auth-dbauth-api": "7.0.0", - "@cedarjs/graphql-server": "7.0.0" + "@cedarjs/api": "0.0.5", + "@cedarjs/auth-dbauth-api": "0.0.5", + "@cedarjs/graphql-server": "0.0.5" } } diff --git a/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.test.ts b/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.test.ts index dd552cafa7..83b683768b 100644 --- a/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.test.ts +++ b/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.test.ts @@ -1,6 +1,6 @@ import { mockRedwoodDirective, getDirectiveName } from '@cedarjs/testing/api' -import requireAuth from './requireAuth' +import requireAuth from './requireAuth.js' describe('requireAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.ts b/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.ts index aa00bc0513..f42ba11d60 100644 --- a/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.ts +++ b/__fixtures__/fragment-test-project/api/src/directives/requireAuth/requireAuth.ts @@ -3,7 +3,7 @@ import gql from 'graphql-tag' import type { ValidatorDirectiveFunc } from '@cedarjs/graphql-server' import { createValidatorDirective } from '@cedarjs/graphql-server' -import { requireAuth as applicationRequireAuth } from 'src/lib/auth' +import { requireAuth as applicationRequireAuth } from 'src/lib/auth.js' export const schema = gql` """ diff --git a/__fixtures__/fragment-test-project/api/src/directives/skipAuth/skipAuth.test.ts b/__fixtures__/fragment-test-project/api/src/directives/skipAuth/skipAuth.test.ts index 65465cd5c8..68c006bdae 100644 --- a/__fixtures__/fragment-test-project/api/src/directives/skipAuth/skipAuth.test.ts +++ b/__fixtures__/fragment-test-project/api/src/directives/skipAuth/skipAuth.test.ts @@ -1,6 +1,6 @@ import { getDirectiveName } from '@cedarjs/testing/api' -import skipAuth from './skipAuth' +import skipAuth from './skipAuth.js' describe('skipAuth directive', () => { it('declares the directive sdl as schema, with the correct name', () => { diff --git a/__fixtures__/fragment-test-project/api/src/functions/auth.ts b/__fixtures__/fragment-test-project/api/src/functions/auth.ts index 149164b1c5..b2192a0dcf 100644 --- a/__fixtures__/fragment-test-project/api/src/functions/auth.ts +++ b/__fixtures__/fragment-test-project/api/src/functions/auth.ts @@ -3,8 +3,8 @@ import type { APIGatewayProxyEvent, Context } from 'aws-lambda' import { DbAuthHandler } from '@cedarjs/auth-dbauth-api' import type { DbAuthHandlerOptions, UserType } from '@cedarjs/auth-dbauth-api' -import { cookieName } from 'src/lib/auth' -import { db } from 'src/lib/db' +import { cookieName } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' export const handler = async ( event: APIGatewayProxyEvent, diff --git a/__fixtures__/fragment-test-project/api/src/functions/graphql.ts b/__fixtures__/fragment-test-project/api/src/functions/graphql.ts index 7b1d7d542a..4445ef3976 100644 --- a/__fixtures__/fragment-test-project/api/src/functions/graphql.ts +++ b/__fixtures__/fragment-test-project/api/src/functions/graphql.ts @@ -5,9 +5,9 @@ import directives from 'src/directives/**/*.{js,ts}' import sdls from 'src/graphql/**/*.sdl.{js,ts}' import services from 'src/services/**/*.{js,ts}' -import { cookieName, getCurrentUser } from 'src/lib/auth' -import { db } from 'src/lib/db' -import { logger } from 'src/lib/logger' +import { cookieName, getCurrentUser } from 'src/lib/auth.js' +import { db } from 'src/lib/db.js' +import { logger } from 'src/lib/logger.js' const authDecoder = createAuthDecoder(cookieName) diff --git a/__fixtures__/fragment-test-project/api/src/graphql/produces.sdl.ts b/__fixtures__/fragment-test-project/api/src/graphql/produces.sdl.ts index 1a3342f662..ac0e6e8fdc 100644 --- a/__fixtures__/fragment-test-project/api/src/graphql/produces.sdl.ts +++ b/__fixtures__/fragment-test-project/api/src/graphql/produces.sdl.ts @@ -51,4 +51,4 @@ export const schema = gql` @skipAuth deleteProduce(id: String!): Produce! @skipAuth } -` +`; diff --git a/__fixtures__/fragment-test-project/api/src/graphql/stalls.sdl.ts b/__fixtures__/fragment-test-project/api/src/graphql/stalls.sdl.ts index c934eeb4a5..a2539a1abb 100644 --- a/__fixtures__/fragment-test-project/api/src/graphql/stalls.sdl.ts +++ b/__fixtures__/fragment-test-project/api/src/graphql/stalls.sdl.ts @@ -26,4 +26,4 @@ export const schema = gql` updateStall(id: String!, input: UpdateStallInput!): Stall! @requireAuth deleteStall(id: String!): Stall! @requireAuth } -` +`; diff --git a/__fixtures__/fragment-test-project/api/src/lib/auth.ts b/__fixtures__/fragment-test-project/api/src/lib/auth.ts index 90e6400ed9..e7eafb1ef6 100644 --- a/__fixtures__/fragment-test-project/api/src/lib/auth.ts +++ b/__fixtures__/fragment-test-project/api/src/lib/auth.ts @@ -1,7 +1,7 @@ import type { Decoded } from '@cedarjs/api' import { AuthenticationError, ForbiddenError } from '@cedarjs/graphql-server' -import { db } from './db' +import { db } from './db.js' /** * The name of the cookie that dbAuth sets diff --git a/__fixtures__/fragment-test-project/api/src/lib/db.ts b/__fixtures__/fragment-test-project/api/src/lib/db.ts index 276431649a..b6fa93228f 100644 --- a/__fixtures__/fragment-test-project/api/src/lib/db.ts +++ b/__fixtures__/fragment-test-project/api/src/lib/db.ts @@ -5,17 +5,22 @@ import { PrismaClient } from '@prisma/client' import { emitLogLevels, handlePrismaLogging } from '@cedarjs/api/logger' -import { logger } from './logger' +import { logger } from './logger.js' -/* - * Instance of the Prisma Client - */ -export const db = new PrismaClient({ +const prismaClient = new PrismaClient({ log: emitLogLevels(['info', 'warn', 'error']), }) handlePrismaLogging({ - db, + 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/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.test.ts b/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.test.ts index 8bb328e175..30c1b833b0 100644 --- a/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.test.ts @@ -6,8 +6,8 @@ import { createContact, updateContact, deleteContact, -} from './contacts' -import type { StandardScenario } from './contacts.scenarios' +} from './contacts.js' +import type { StandardScenario } from './contacts.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -39,7 +39,9 @@ describe('contacts', () => { }) scenario('updates a contact', async (scenario: StandardScenario) => { - const original = (await contact({ id: scenario.contact.one.id })) as Contact + const original = (await contact({ + id: scenario.contact.one.id, + })) as Contact const result = await updateContact({ id: original.id, input: { name: 'String2' }, diff --git a/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.ts b/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.ts index 453651e37b..b041558483 100644 --- a/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.ts +++ b/__fixtures__/fragment-test-project/api/src/services/contacts/contacts.ts @@ -1,6 +1,6 @@ import type { QueryResolvers, MutationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const contacts: QueryResolvers['contacts'] = () => { return db.contact.findMany() diff --git a/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.scenarios.ts new file mode 100644 index 0000000000..99271967d8 --- /dev/null +++ b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.scenarios.ts @@ -0,0 +1,12 @@ +import type { Prisma, Contact } from '@prisma/client' + +import type { ScenarioData } from '@cedarjs/testing/api' + +export const standard = defineScenario({ + contact: { + one: { data: { name: 'String', email: 'String', message: 'String' } }, + two: { data: { name: 'String', email: 'String', message: 'String' } }, + }, +}) + +export type StandardScenario = ScenarioData diff --git a/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts new file mode 100644 index 0000000000..701c9408ab --- /dev/null +++ b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts @@ -0,0 +1,57 @@ +import { db } from 'src/lib/db' + +import { contact, contacts, createContact } from './contacts' +import type { StandardScenario } from './contacts.scenarios' + +/** + * Example test for describe scenario. + * + * Note that scenario tests need a matching [name].scenarios.ts file. + */ + +describeScenario('contacts', (getScenario) => { + let scenario: StandardScenario + + beforeEach(() => { + scenario = getScenario() + }) + + it('returns all contacts', async () => { + const result = await contacts() + + expect(result.length).toEqual(Object.keys(scenario.contact).length) + }) + + it('returns a single contact', async () => { + const result = await contact({ id: scenario.contact.one.id }) + + expect(result).toEqual(scenario.contact.one) + }) + + it('creates a contact', async () => { + const result = await createContact({ + input: { + name: 'Bazinga', + email: 'contact@describe.scenario', + message: 'Describe scenario works!', + }, + }) + + expect(result.name).toEqual('Bazinga') + expect(result.email).toEqual('contact@describe.scenario') + expect(result.message).toEqual('Describe scenario works!') + }) + + it('Checking that describe scenario works', async () => { + // This test is dependent on the above test. If you used a normal scenario it would not work + const contactCreatedInAboveTest = await db.contact.findFirst({ + where: { + email: 'contact@describe.scenario', + }, + }) + + expect(contactCreatedInAboveTest.message).toEqual( + 'Describe scenario works!' + ) + }) +}) diff --git a/__fixtures__/fragment-test-project/api/src/services/posts/posts.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/posts/posts.scenarios.ts index 574a453df8..6890e49ad1 100644 --- a/__fixtures__/fragment-test-project/api/src/services/posts/posts.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/posts/posts.scenarios.ts @@ -10,7 +10,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String12', + email: 'String13', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -24,7 +24,7 @@ export const standard = defineScenario({ body: 'String', author: { create: { - email: 'String68383', + email: 'String27', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/fragment-test-project/api/src/services/posts/posts.test.ts b/__fixtures__/fragment-test-project/api/src/services/posts/posts.test.ts index fc316574f5..b7c7572341 100644 --- a/__fixtures__/fragment-test-project/api/src/services/posts/posts.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/posts/posts.test.ts @@ -1,7 +1,7 @@ import type { Post } from '@prisma/client' -import { posts, post, createPost, updatePost, deletePost } from './posts' -import type { StandardScenario } from './posts.scenarios' +import { posts, post, createPost, updatePost, deletePost } from './posts.js' +import type { StandardScenario } from './posts.scenarios.js' // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. diff --git a/__fixtures__/fragment-test-project/api/src/services/posts/posts.ts b/__fixtures__/fragment-test-project/api/src/services/posts/posts.ts index eaeff31fb7..13df31ed97 100644 --- a/__fixtures__/fragment-test-project/api/src/services/posts/posts.ts +++ b/__fixtures__/fragment-test-project/api/src/services/posts/posts.ts @@ -4,7 +4,7 @@ import type { PostRelationResolvers, } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export const posts: QueryResolvers['posts'] = () => { return db.post.findMany() diff --git a/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts index eb0348e401..0773813183 100644 --- a/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts @@ -1,27 +1,28 @@ -import type { Prisma, Produce } from '@prisma/client' -import type { ScenarioData } from '@cedarjs/testing/api' +import type { Prisma, Produce } from "@prisma/client"; + +import type { ScenarioData } from "@cedarjs/testing/api"; export const standard = defineScenario({ produce: { one: { data: { - name: 'String6430168', - quantity: 7893718, - price: 1113110, - region: 'String', - stall: { create: { name: 'String', stallNumber: 'String1437797' } }, + name: "String4372567", + quantity: 5121815, + price: 1146204, + region: "String", + stall: { create: { name: "String", stallNumber: "String4356419" } }, }, }, two: { data: { - name: 'String2325729', - quantity: 9170370, - price: 9020391, - region: 'String', - stall: { create: { name: 'String', stallNumber: 'String8553241' } }, + name: "String1795238", + quantity: 2348895, + price: 3710401, + region: "String", + stall: { create: { name: "String", stallNumber: "String8563946" } }, }, }, }, -}) +}); -export type StandardScenario = ScenarioData +export type StandardScenario = ScenarioData; diff --git a/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts b/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts index 38e3457c11..3de9397ddb 100644 --- a/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts @@ -1,4 +1,4 @@ -import type { Produce } from '@prisma/client' +import type { Produce } from "@prisma/client"; import { produces, @@ -6,8 +6,8 @@ import { createProduce, updateProduce, deleteProduce, -} from './produces' -import type { StandardScenario } from './produces.scenarios' +} from "./produces.js"; +import type { StandardScenario } from "./produces.scenarios.js"; // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -15,53 +15,55 @@ import type { StandardScenario } from './produces.scenarios' // https://redwoodjs.com/docs/testing#testing-services // https://redwoodjs.com/docs/testing#jest-expect-type-considerations -describe('produces', () => { - scenario('returns all produces', async (scenario: StandardScenario) => { - const result = await produces() +describe("produces", () => { + scenario("returns all produces", async (scenario: StandardScenario) => { + const result = await produces(); - expect(result.length).toEqual(Object.keys(scenario.produce).length) - }) + expect(result.length).toEqual(Object.keys(scenario.produce).length); + }); - scenario('returns a single produce', async (scenario: StandardScenario) => { - const result = await produce({ id: scenario.produce.one.id }) + scenario("returns a single produce", async (scenario: StandardScenario) => { + const result = await produce({ id: scenario.produce.one.id }); - expect(result).toEqual(scenario.produce.one) - }) + expect(result).toEqual(scenario.produce.one); + }); - scenario('creates a produce', async (scenario: StandardScenario) => { + scenario("creates a produce", async (scenario: StandardScenario) => { const result = await createProduce({ input: { - name: 'String9956690', - quantity: 4900208, - price: 6716153, - region: 'String', + name: "String525661", + quantity: 4885196, + price: 2377066, + region: "String", stallId: scenario.produce.two.stallId, }, - }) + }); - expect(result.name).toEqual('String9956690') - expect(result.quantity).toEqual(4900208) - expect(result.price).toEqual(6716153) - expect(result.region).toEqual('String') - expect(result.stallId).toEqual(scenario.produce.two.stallId) - }) + expect(result.name).toEqual("String525661"); + expect(result.quantity).toEqual(4885196); + expect(result.price).toEqual(2377066); + expect(result.region).toEqual("String"); + expect(result.stallId).toEqual(scenario.produce.two.stallId); + }); - scenario('updates a produce', async (scenario: StandardScenario) => { - const original = (await produce({ id: scenario.produce.one.id })) as Produce + scenario("updates a produce", async (scenario: StandardScenario) => { + const original = (await produce({ + id: scenario.produce.one.id, + })) as Produce; const result = await updateProduce({ id: original.id, - input: { name: 'String87087152' }, - }) + input: { name: "String87747082" }, + }); - expect(result.name).toEqual('String87087152') - }) + expect(result.name).toEqual("String87747082"); + }); - scenario('deletes a produce', async (scenario: StandardScenario) => { + scenario("deletes a produce", async (scenario: StandardScenario) => { const original = (await deleteProduce({ id: scenario.produce.one.id, - })) as Produce - const result = await produce({ id: original.id }) + })) as Produce; + const result = await produce({ id: original.id }); - expect(result).toEqual(null) - }) -}) + expect(result).toEqual(null); + }); +}); diff --git a/__fixtures__/fragment-test-project/api/src/services/produces/produces.ts b/__fixtures__/fragment-test-project/api/src/services/produces/produces.ts index 5e99407a6d..962ac40459 100644 --- a/__fixtures__/fragment-test-project/api/src/services/produces/produces.ts +++ b/__fixtures__/fragment-test-project/api/src/services/produces/produces.ts @@ -2,46 +2,46 @@ import type { QueryResolvers, MutationResolvers, ProduceRelationResolvers, -} from 'types/graphql' +} from "types/graphql"; -import { db } from 'src/lib/db' +import { db } from "src/lib/db.js"; -export const produces: QueryResolvers['produces'] = () => { - return db.produce.findMany() -} +export const produces: QueryResolvers["produces"] = () => { + return db.produce.findMany(); +}; -export const produce: QueryResolvers['produce'] = ({ id }) => { +export const produce: QueryResolvers["produce"] = ({ id }) => { return db.produce.findUnique({ where: { id }, - }) -} + }); +}; -export const createProduce: MutationResolvers['createProduce'] = ({ +export const createProduce: MutationResolvers["createProduce"] = ({ input, }) => { return db.produce.create({ data: input, - }) -} + }); +}; -export const updateProduce: MutationResolvers['updateProduce'] = ({ +export const updateProduce: MutationResolvers["updateProduce"] = ({ id, input, }) => { return db.produce.update({ data: input, where: { id }, - }) -} + }); +}; -export const deleteProduce: MutationResolvers['deleteProduce'] = ({ id }) => { +export const deleteProduce: MutationResolvers["deleteProduce"] = ({ id }) => { return db.produce.delete({ where: { id }, - }) -} + }); +}; export const Produce: ProduceRelationResolvers = { stall: (_obj, { root }) => { - return db.produce.findUnique({ where: { id: root?.id } }).stall() + return db.produce.findUnique({ where: { id: root?.id } }).stall(); }, -} +}; diff --git a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts index 5083a4074c..41bb725b6e 100644 --- a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts @@ -1,11 +1,12 @@ -import type { Prisma, Stall } from '@prisma/client' -import type { ScenarioData } from '@cedarjs/testing/api' +import type { Prisma, Stall } from "@prisma/client"; + +import type { ScenarioData } from "@cedarjs/testing/api"; export const standard = defineScenario({ stall: { - one: { data: { name: 'String', stallNumber: 'String3227467' } }, - two: { data: { name: 'String', stallNumber: 'String3142426' } }, + one: { data: { name: "String", stallNumber: "String423538" } }, + two: { data: { name: "String", stallNumber: "String5801194" } }, }, -}) +}); -export type StandardScenario = ScenarioData +export type StandardScenario = ScenarioData; diff --git a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts index e527a42691..377f81c7e3 100644 --- a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts @@ -1,7 +1,13 @@ -import type { Stall } from '@prisma/client' +import type { Stall } from "@prisma/client"; -import { stalls, stall, createStall, updateStall, deleteStall } from './stalls' -import type { StandardScenario } from './stalls.scenarios' +import { + stalls, + stall, + createStall, + updateStall, + deleteStall, +} from "./stalls.js"; +import type { StandardScenario } from "./stalls.scenarios.js"; // Generated boilerplate tests do not account for all circumstances // and can fail without adjustments, e.g. Float. @@ -9,42 +15,44 @@ import type { StandardScenario } from './stalls.scenarios' // https://redwoodjs.com/docs/testing#testing-services // https://redwoodjs.com/docs/testing#jest-expect-type-considerations -describe('stalls', () => { - scenario('returns all stalls', async (scenario: StandardScenario) => { - const result = await stalls() +describe("stalls", () => { + scenario("returns all stalls", async (scenario: StandardScenario) => { + const result = await stalls(); - expect(result.length).toEqual(Object.keys(scenario.stall).length) - }) + expect(result.length).toEqual(Object.keys(scenario.stall).length); + }); - scenario('returns a single stall', async (scenario: StandardScenario) => { - const result = await stall({ id: scenario.stall.one.id }) + scenario("returns a single stall", async (scenario: StandardScenario) => { + const result = await stall({ id: scenario.stall.one.id }); - expect(result).toEqual(scenario.stall.one) - }) + expect(result).toEqual(scenario.stall.one); + }); - scenario('creates a stall', async () => { + scenario("creates a stall", async () => { const result = await createStall({ - input: { name: 'String', stallNumber: 'String7681055' }, - }) + input: { name: "String", stallNumber: "String7454580" }, + }); - expect(result.name).toEqual('String') - expect(result.stallNumber).toEqual('String7681055') - }) + expect(result.name).toEqual("String"); + expect(result.stallNumber).toEqual("String7454580"); + }); - scenario('updates a stall', async (scenario: StandardScenario) => { - const original = (await stall({ id: scenario.stall.one.id })) as Stall + scenario("updates a stall", async (scenario: StandardScenario) => { + const original = (await stall({ id: scenario.stall.one.id })) as Stall; const result = await updateStall({ id: original.id, - input: { name: 'String2' }, - }) + input: { name: "String2" }, + }); - expect(result.name).toEqual('String2') - }) + expect(result.name).toEqual("String2"); + }); - scenario('deletes a stall', async (scenario: StandardScenario) => { - const original = (await deleteStall({ id: scenario.stall.one.id })) as Stall - const result = await stall({ id: original.id }) + scenario("deletes a stall", async (scenario: StandardScenario) => { + const original = (await deleteStall({ + id: scenario.stall.one.id, + })) as Stall; + const result = await stall({ id: original.id }); - expect(result).toEqual(null) - }) -}) + expect(result).toEqual(null); + }); +}); diff --git a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.ts b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.ts index 91c7ce00cd..e673987dbf 100644 --- a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.ts +++ b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.ts @@ -2,44 +2,44 @@ import type { QueryResolvers, MutationResolvers, StallRelationResolvers, -} from 'types/graphql' +} from "types/graphql"; -import { db } from 'src/lib/db' +import { db } from "src/lib/db.js"; -export const stalls: QueryResolvers['stalls'] = () => { - return db.stall.findMany() -} +export const stalls: QueryResolvers["stalls"] = () => { + return db.stall.findMany(); +}; -export const stall: QueryResolvers['stall'] = ({ id }) => { +export const stall: QueryResolvers["stall"] = ({ id }) => { return db.stall.findUnique({ where: { id }, - }) -} + }); +}; -export const createStall: MutationResolvers['createStall'] = ({ input }) => { +export const createStall: MutationResolvers["createStall"] = ({ input }) => { return db.stall.create({ data: input, - }) -} + }); +}; -export const updateStall: MutationResolvers['updateStall'] = ({ +export const updateStall: MutationResolvers["updateStall"] = ({ id, input, }) => { return db.stall.update({ data: input, where: { id }, - }) -} + }); +}; -export const deleteStall: MutationResolvers['deleteStall'] = ({ id }) => { +export const deleteStall: MutationResolvers["deleteStall"] = ({ id }) => { return db.stall.delete({ where: { id }, - }) -} + }); +}; export const Stall: StallRelationResolvers = { produce: (_obj, { root }) => { - return db.stall.findUnique({ where: { id: root?.id } }).produce() + return db.stall.findUnique({ where: { id: root?.id } }).produce(); }, -} +}; diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.scenarios.ts index 756c45b521..159e3cabd9 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.scenarios.ts @@ -6,7 +6,7 @@ export const standard = defineScenario({ user: { one: { data: { - email: 'String8', + email: 'String9', hashedPassword: 'String', fullName: 'String', salt: 'String', @@ -14,7 +14,7 @@ export const standard = defineScenario({ }, two: { data: { - email: 'String16', + email: 'String17', hashedPassword: 'String', fullName: 'String', salt: 'String', diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.ts index 1160d12f84..380ef92c64 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.ts @@ -1,6 +1,6 @@ import type { QueryResolvers, UserRelationResolvers } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' export {} diff --git a/__fixtures__/fragment-test-project/api/tsconfig.json b/__fixtures__/fragment-test-project/api/tsconfig.json index f2905204fe..c9075d6fac 100644 --- a/__fixtures__/fragment-test-project/api/tsconfig.json +++ b/__fixtures__/fragment-test-project/api/tsconfig.json @@ -3,27 +3,17 @@ "noEmit": true, "allowJs": true, "esModuleInterop": true, - "target": "esnext", - "module": "esnext", - "moduleResolution": "node", + "target": "ES2023", + "module": "Node16", + "moduleResolution": "Node16", "skipLibCheck": false, - "baseUrl": "./", - "rootDirs": [ - "./src", - "../.redwood/types/mirror/api/src" - ], + "rootDirs": ["./src", "../.redwood/types/mirror/api/src"], "paths": { - "src/*": [ - "./src/*", - "../.redwood/types/mirror/api/src/*" - ], + "src/*": ["./src/*", "../.redwood/types/mirror/api/src/*"], "types/*": ["./types/*", "../types/*"], "@cedarjs/testing": ["../node_modules/@cedarjs/testing/api"] }, - "typeRoots": [ - "../node_modules/@types", - "./node_modules/@types" - ], + "typeRoots": ["../node_modules/@types", "./node_modules/@types"], "types": ["jest"], "jsx": "react-jsx" }, diff --git a/__fixtures__/fragment-test-project/api/vitest.config.ts b/__fixtures__/fragment-test-project/api/vitest.config.ts new file mode 100644 index 0000000000..f3de348f75 --- /dev/null +++ b/__fixtures__/fragment-test-project/api/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from 'vitest/config' + +import { cedarVitestPreset } from '@cedarjs/vite/api' + +export default defineConfig({ + plugins: [cedarVitestPreset()], + test: { + globals: true, + }, +}) diff --git a/__fixtures__/fragment-test-project/graphql.config.js b/__fixtures__/fragment-test-project/graphql.config.cjs similarity index 100% rename from __fixtures__/fragment-test-project/graphql.config.js rename to __fixtures__/fragment-test-project/graphql.config.cjs diff --git a/__fixtures__/fragment-test-project/jest.config.js b/__fixtures__/fragment-test-project/jest.config.js deleted file mode 100644 index c6b395cb76..0000000000 --- a/__fixtures__/fragment-test-project/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// 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/__fixtures__/fragment-test-project/package.json b/__fixtures__/fragment-test-project/package.json index 88dc64132e..c890d69f0e 100644 --- a/__fixtures__/fragment-test-project/package.json +++ b/__fixtures__/fragment-test-project/package.json @@ -1,5 +1,6 @@ { "private": true, + "type": "module", "workspaces": { "packages": [ "api", @@ -7,8 +8,9 @@ ] }, "devDependencies": { - "@cedarjs/core": "7.0.0", - "@cedarjs/project-config": "7.0.0" + "@cedarjs/core": "0.0.5", + "@cedarjs/project-config": "0.0.5", + "@cedarjs/testing": "0.0.5" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", @@ -20,5 +22,10 @@ "prisma": { "seed": "yarn rw exec seed" }, - "packageManager": "yarn@4.6.0" + "packageManager": "yarn@4.9.2", + "resolutions": { + "@storybook/react-dom-shim@npm:7.6.20": "https://verdaccio.tobbe.dev/@storybook/react-dom-shim/-/react-dom-shim-8.0.8.tgz", + "react-is": "19.0.0-rc-f2df5694-20240916", + "vite": "5.4.16" + } } diff --git a/__fixtures__/fragment-test-project/prettier.config.js b/__fixtures__/fragment-test-project/prettier.config.cjs similarity index 87% rename from __fixtures__/fragment-test-project/prettier.config.js rename to __fixtures__/fragment-test-project/prettier.config.cjs index 3ed0f1e84d..9d81988854 100644 --- a/__fixtures__/fragment-test-project/prettier.config.js +++ b/__fixtures__/fragment-test-project/prettier.config.cjs @@ -15,6 +15,6 @@ module.exports = { }, }, ], - tailwindConfig: './web/config/tailwind.config.js', + tailwindConfig: './web/config/tailwind.config.cjs', plugins: ['prettier-plugin-tailwindcss'], } diff --git a/__fixtures__/fragment-test-project/redwood.toml b/__fixtures__/fragment-test-project/redwood.toml index e21a6085fa..42d996a73f 100644 --- a/__fixtures__/fragment-test-project/redwood.toml +++ b/__fixtures__/fragment-test-project/redwood.toml @@ -6,9 +6,9 @@ # https://redwoodjs.com/docs/app-configuration-redwood-toml [web] - title = "Redwood App" + title = "Cedar App" port = "${WEB_DEV_PORT:8910}" - apiUrl = "/.redwood/functions" # You can customize graphql and dbauth urls individually too: see https://redwoodjs.com/docs/app-configuration-redwood-toml#api-paths + apiUrl = "/.redwood/functions" includeEnvironmentVariables = [ # Add any ENV vars that should be available to the web side to this array # See https://redwoodjs.com/docs/environment-variables#web diff --git a/__fixtures__/fragment-test-project/scripts/seed.ts b/__fixtures__/fragment-test-project/scripts/seed.ts index 4bbf5eb826..ffdb815a6d 100644 --- a/__fixtures__/fragment-test-project/scripts/seed.ts +++ b/__fixtures__/fragment-test-project/scripts/seed.ts @@ -1,5 +1,11 @@ -import type { Prisma } from '@prisma/client' -import { db } from 'api/src/lib/db' +import { db } from 'api/src/lib/db.js' + +// 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 { @@ -138,8 +144,8 @@ export default async () => { // Create your database records here! For example, seed some users: // // const users = [ - // { name: 'Alice', email: 'alice@cedarjs.com }, - // { name: 'Bob', email: 'bob@cedarjs.com }, + // { name: 'Alice', email: 'alice@cedarjs.com' }, + // { name: 'Bob', email: 'bob@cedarjs.com' }, // ] // // await db.user.createMany({ data: users }) diff --git a/__fixtures__/fragment-test-project/scripts/tsconfig.json b/__fixtures__/fragment-test-project/scripts/tsconfig.json index babc7c436b..5690eec16c 100644 --- a/__fixtures__/fragment-test-project/scripts/tsconfig.json +++ b/__fixtures__/fragment-test-project/scripts/tsconfig.json @@ -3,31 +3,18 @@ "noEmit": true, "allowJs": true, "esModuleInterop": true, - "target": "esnext", - "module": "esnext", - "moduleResolution": "node", - "baseUrl": "./", + "target": "ES2023", + "module": "Node16", + "moduleResolution": "Node16", "paths": { - "$api/*": [ - "../api/*" - ], - "api/*": [ - "../api/*" - ], - "$web/*": [ - "../web/*" - ], - "web/*": [ - "../web/*" - ], - "$web/src/*": [ - "../web/src/*", - "../.redwood/types/mirror/web/src/*" - ], - "web/src/*": [ - "../web/src/*", - "../.redwood/types/mirror/web/src/*" - ], + "$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"], diff --git a/__fixtures__/fragment-test-project/vitest.config.ts b/__fixtures__/fragment-test-project/vitest.config.ts new file mode 100644 index 0000000000..79b00e32e9 --- /dev/null +++ b/__fixtures__/fragment-test-project/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vitest/config' + +export default defineConfig({ + test: { + projects: ['/{*,!(node_modules)/**/}/vite*'], + }, +}) diff --git a/__fixtures__/fragment-test-project/web/config/postcss.config.js b/__fixtures__/fragment-test-project/web/config/postcss.config.cjs similarity index 77% rename from __fixtures__/fragment-test-project/web/config/postcss.config.js rename to __fixtures__/fragment-test-project/web/config/postcss.config.cjs index ca420cad42..f6d96e144e 100644 --- a/__fixtures__/fragment-test-project/web/config/postcss.config.js +++ b/__fixtures__/fragment-test-project/web/config/postcss.config.cjs @@ -2,7 +2,8 @@ const path = require('path') module.exports = { plugins: [ - require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.js')), + require('tailwindcss/nesting'), + require('tailwindcss')(path.resolve(__dirname, 'tailwind.config.cjs')), require('autoprefixer'), ], } diff --git a/__fixtures__/fragment-test-project/web/config/tailwind.config.cjs b/__fixtures__/fragment-test-project/web/config/tailwind.config.cjs new file mode 100644 index 0000000000..383b9a16be --- /dev/null +++ b/__fixtures__/fragment-test-project/web/config/tailwind.config.cjs @@ -0,0 +1,10 @@ +const { join } = require('node:path') + +/** @type {import('tailwindcss').Config} */ +export default { + content: [join(__dirname, '../src/**/*.{js,jsx,ts,tsx}')], + theme: { + extend: {}, + }, + plugins: [], +} diff --git a/__fixtures__/fragment-test-project/web/config/tailwind.config.js b/__fixtures__/fragment-test-project/web/config/tailwind.config.js deleted file mode 100644 index baf368f417..0000000000 --- a/__fixtures__/fragment-test-project/web/config/tailwind.config.js +++ /dev/null @@ -1,8 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -module.exports = { - content: ['src/**/*.{js,jsx,ts,tsx}'], - theme: { - extend: {}, - }, - plugins: [], -} diff --git a/__fixtures__/fragment-test-project/web/jest.config.js b/__fixtures__/fragment-test-project/web/jest.config.js deleted file mode 100644 index cd4ee4403c..0000000000 --- a/__fixtures__/fragment-test-project/web/jest.config.js +++ /dev/null @@ -1,8 +0,0 @@ -// More info at https://redwoodjs.com/docs/project-configuration-dev-test-build - -const config = { - rootDir: '../', - preset: '@cedarjs/testing/config/jest/web', -} - -module.exports = config diff --git a/__fixtures__/fragment-test-project/web/package.json b/__fixtures__/fragment-test-project/web/package.json index 49b39194bc..63050afa37 100644 --- a/__fixtures__/fragment-test-project/web/package.json +++ b/__fixtures__/fragment-test-project/web/package.json @@ -1,5 +1,6 @@ { "name": "web", + "type": "module", "version": "0.0.0", "private": true, "browserslist": { @@ -11,22 +12,21 @@ ] }, "dependencies": { - "@cedarjs/auth-dbauth-web": "7.0.0", - "@cedarjs/forms": "7.0.0", - "@cedarjs/router": "7.0.0", - "@cedarjs/web": "7.0.0", + "@cedarjs/auth-dbauth-web": "0.0.5", + "@cedarjs/forms": "0.0.5", + "@cedarjs/router": "0.0.5", + "@cedarjs/web": "0.0.5", "humanize-string": "2.1.0", "react": "19.0.0-rc-f2df5694-20240916", "react-dom": "19.0.0-rc-f2df5694-20240916" }, "devDependencies": { - "@cedarjs/vite": "7.0.0", - "@types/react": "18.2.37", - "@types/react-dom": "18.2.15", - "autoprefixer": "^10.4.16", - "postcss": "^8.4.33", - "postcss-loader": "^7.3.4", - "prettier-plugin-tailwindcss": "^0.5.12", - "tailwindcss": "^3.4.1" + "@cedarjs/vite": "0.0.5", + "@types/react": "^18.2.55", + "@types/react-dom": "^18.2.19", + "autoprefixer": "^10.4.21", + "postcss": "^8.5.6", + "postcss-loader": "^8.1.1", + "tailwindcss": "^3.4.17" } } diff --git a/__fixtures__/fragment-test-project/web/public/README.md b/__fixtures__/fragment-test-project/web/public/README.md index 618395f020..1b09bf8361 100644 --- a/__fixtures__/fragment-test-project/web/public/README.md +++ b/__fixtures__/fragment-test-project/web/public/README.md @@ -1,25 +1,33 @@ # 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 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. + +> Note: files will _not_ hot reload while the development server is running. You'll need to manually stop/start to access file changes. ### Example Use + A file like `favicon.png` will be copied to `/dist/favicon.png`. A folder containing a file such as `static-files/my-logo.jpg` will be copied to `/dist/static-files/my-logo.jpg`. These can be referenced in your code directly without any special handling, e.g. + ``` ``` + and + ``` alt="Logo" /> ``` - ## 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 Vite, etc. 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 Vite + Instead of handling our logo image as a static file per the example above, we can do the following: + ``` import React from "react" import logo from "./my-logo.jpg" diff --git a/__fixtures__/fragment-test-project/web/public/favicon.png b/__fixtures__/fragment-test-project/web/public/favicon.png index 47414294173cb0795dcafb8813599fc382282556..1232ba73ab4eaff34dd96755634fb0c5f8e2267f 100644 GIT binary patch literal 2500 zcmZ`*2{_bi7k_6g4VfZYQZ#Wbam|cjEMsQu8p~X33>VE{(#R~$j3OW?A$pCG1({wssjLJCzgI-$VrV66kjR;I}HFxOa|aRq)L1aKr9x3 z5jp^bOaL@^;_E&{$Z(ZObK6X%0%J%<06CZzkVb$(Ta;Hq zGXD?@$U8B9wkTsz*1Ey!9bQ6Di6+Q`O}0n zswW|W%SJOIx#3K79GfRa0b(2h64^{40~N=P;RpzEB;7?10whb-7+ut&i*PGRmqztQ zIdS<+lqDLA#_Ez4Q79CVAGw*}>+Jdo4(&*~TZBR$0fX7GV+VSNIhxCl!kF3E*kG_Y z3=U@sd6){~IYLI9DMz69G05jQ&P+iBpT!fhxEz!;E+d>9DSHn@bTkAfJ~SvU-=E8kA^)Gz5i#F8 zel2)029+Dh+8po95HiWqDVt&~O|e$~pQrp0{*BK!XlY%9AYZ0{8xt!%7P1wNi1{Do z_W&m@o5yDg1X2M0lmEB6@BL~2l;B(CCjb#6oys>;`gjEvwa^_PD?+V&zE5Pulec_^ z0gwl7&JO-Gh3dc{ia2WJoa+Yd*!tZ)({jc22eu_Yj#klA+t03@bScy?lw%$7Za!r- z>s^3CL+~p+O~Z6}c({9ifBz`#@PlC6D{%Y17cXXW^7He@rl*_Y zUx5{>sy&laQ_JkgWc9qfyw`ti2P_ur9KpusNLrd|-^fT-@960KK|JcR`@W&8trZ%&d!#b+H!e!bjaS8aZ9BwckkZS(R<&ddkd~@S63%HcUIfl+E&R9=n@FQk~5NWl%_YPzP>&=>`Dc4*|I(GuF(u| zHFIB0O^t(f=b;WE?;>N=jii0^5-m9R6w4Z_OG`%wos*R{^1E!Jix$Pr%`JH*=aRoM z^4{p!n0I4SlLxY+tII=sjBEl28^glF?0SQHp!46Qia4f8qtUPuUS@W7=nAdlLRswo zs#cjb+u<>=Oe2!RCK0$N`ZiTt+5sU1`O|`J^P7hb=97cr2NW_H#hh z*W~&e2O;->N^5g-^P|&V_hCMn3A{Z$MU|B^6RoYSnA}OkQEpV!h743THhKzh*0{eE z(cKf*pV2AJK6`d`aY;$a?NjF0ng@r2k-1Om>+0(Ru9TGAiQXr1rqMDxo;*=&B)K%a z8Nk0mPfbr38U?9&-ma^wd)C|A8~9ADdaqoI78cfSg~yW)R<`DbdW<}jnK!i!Q$^V7 zYQI@SmlM0I(>JNDTv;IFhrF21Bv?FY#DT}EI2?|AR3!Ry`AyhL4LxQ_TJRvP2#zW= zx&)h4sNK4D?b=JKc6N4UWo0UgbCF6)N|RZH;qoB))LC>%z}aJje_w59v)L~8_KC8! zYXpXKqO3N_)(k_uB-T^LBdrN$d7-4_Rl-b{nH)nMM9(N>-4`_PT{0QZkc{2i@GSGB zZ*q+NjCyMSKuGU{=T=r$h4y#kg?y2JHO1YXe_M%>@U#lie?8#Ox&zqaBF685K@*;G z`4Mv1{$GAkGY-w|9u!#Wxl$#>^Pwg!EiF9IZao!%t0PWO3?mhjJaSaQ8M^Y!0_s=U%iV_(^{_$Z!?GbT z`toF$=-^55-toKTF_uE-2n$cDvN9}HVM^Zh-m2O+k*5px(9FbZl}A?PtTr$-+-pG? z9$z6dG&Hoysl?bd<%j_?@qH+^-_y`@$@Olf^RYKbUdwbXjPEFBYn4l;gYYkr;+z;d z5{Y~_H#g_iCpdRJrAr_1ly5X7hV^uG%;@i&bx25v zT~0_o3}5x`8hX9~dInsJ(?w#sh-LrSV4Xz8x_VHI^1P7ew|)C|zCbYHLF^iWa~if9 zYg)UG&c7c%u58rOGxpj@!+cYQuECJi#mtlsA3hi>mgeV2&@QyxO2s=r4e|4vI6nU5 z*liD`-dY`TMM#KbqO`Qs&m#n~k2vH^NvMo|d+ePaG7?=a=CVO%0_lZ`rW=_K8R;lDM(&q7uB~qD03d;*1K{H6`dZ5`8FMhF z$$mC=_l6qdmw&Tloxz`{FA)gU4j51I^yPT#5+A1K=Ah;@6E*Y7egmPvTy%Cuj#-1P*+2S*CLdE1DZSjgy@VQH2$`1GU+#rg8F zR$YwZD}Nr~mN?3t1H$+fJ*q^w!}8`oOx{i(WL4oLgKN0~^gQyJ3t#+tnI zhR=h}6@BVu1&_1g7*O6j$-5z)KLsPi3dqCHq+n<+)2a$Afvr|B97(#s5f6-oU6qYH zP<2rWEKfC)aEc=?j9nPwEyIiT4XC9rC}S&)2T1E#ssMJ^LztO+3KfwL5X{>M zSVbgW&Lc!k)10d+!kf&hqFE|9Vh>7ln^Du^lymZgp@If_T}Y!HOs4s~sx97EE}f#f zqJJa0rQ#NtfWL1g`JRKWrhzkOz}3JXFv``3vJ>e$!tR zU0T=ZBw;6Fcf<+WJFl?jYuHIHe#iO8oPVX~ClfsVZqz@)8TX&lY;hgD*?ak`i;G58 z)Unx}1HsXE&~OP1<9SR7KVqB3$1%mbxCZ;Sp>$M9Yp(B8bacJX#0*lw*tn96UxD2t zeYiPx=#1SmQ@me;pVl*ibiW6hk!R+L$P}3mHTI_7u+k9d_?db{`}fWW+FU&GKd9pW?cv0e8pA%20doi=OgaTV=d(hLLf~*OI>?bK)?}CLp?@fx&qv2& z8nTu)Zgq@nK-g?h{TSDLz?*U78c2jd1F(mz;hp^J>@)olqKW8N*!N%PP0mqlwJ=}f zAx`wBC14?1;rG3pfVpuyAnRG{qB@m<4sjx|3QXJ;oL-g>8CmK##c(Fvlbs{&mQi zes$w-ht;Jg<}KQ-xQO$0-U--7E9<0Mu$>LaMpf|y{txjegQcJJL*X+^@Iu4kR5`bj z9!k!ug?a6r=QYP`RSyx%w${>U-%Ti~`7ng143&p=YSg$@(}o^7cYi7uFV`PUvUKk< zzHr;i8;_?lTDE=VAdG9#d?>4gU&nzX&+gTEm1bNsCdaXcvaOny-3X43Fs?Jn;>*U? zjaR1`9KIVP?p(?ulraQZc;T0UKos^SChGJoJYVu1%?E0vDGNOfZKPrPKtyFYEU~bZ zZ~rB{4X2ko>_VJlJvTkUQ{Ofi<63r5H{kM3j=PG9wt=M;^}$>alMVGQ`T&9d09lY& U^>nb41ONa407*qoM6N<$f

- +

Redwood

diff --git a/__fixtures__/fragment-test-project/web/src/Routes.tsx b/__fixtures__/fragment-test-project/web/src/Routes.tsx index def0dd8aa9..157454cad9 100644 --- a/__fixtures__/fragment-test-project/web/src/Routes.tsx +++ b/__fixtures__/fragment-test-project/web/src/Routes.tsx @@ -7,13 +7,13 @@ // 'src/pages/HomePage/HomePage.js' -> HomePage // 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage -import { Router, Route, PrivateSet, Set } from '@cedarjs/router' +import { Router, Route, Private, Set } from '@cedarjs/router' import BlogLayout from 'src/layouts/BlogLayout' import ScaffoldLayout from 'src/layouts/ScaffoldLayout' import HomePage from 'src/pages/HomePage' -import { useAuth } from './auth' +import { useAuth } from './auth.js' const Routes = () => { return ( @@ -38,9 +38,9 @@ const Routes = () => { - + - + diff --git a/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.mock.ts b/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.mock.ts index 4473d93d09..5dd8454b3d 100644 --- a/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.mock.ts +++ b/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.mock.ts @@ -1,6 +1,7 @@ // Define your own mock data here: export const standard = (/* vars, { ctx, req } */) => ({ author: { + __typename: 'User' as const, id: 42, email: 'fortytwo@42.com', fullName: 'Forty Two', diff --git a/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.test.tsx b/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.test.tsx index 9080fe6182..4fbec70cb1 100644 --- a/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.test.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/AuthorCell/AuthorCell.test.tsx @@ -24,7 +24,7 @@ describe('AuthorCell', () => { it('renders Failure successfully', async () => { expect(() => { - render() + render() }).not.toThrow() }) @@ -36,7 +36,7 @@ describe('AuthorCell', () => { it('renders Success successfully', async () => { expect(() => { - render() + render() }).not.toThrow() }) }) diff --git a/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.mock.ts b/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.mock.ts index a42723a44f..e805ec44db 100644 --- a/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.mock.ts +++ b/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.mock.ts @@ -1,6 +1,7 @@ // Define your own mock data here: export const standard = (/* vars, { ctx, req } */) => ({ blogPost: { + __typename: 'Post' as const, id: 42, title: 'Mocked title', body: 'Mocked body', @@ -8,6 +9,7 @@ export const standard = (/* vars, { ctx, req } */) => ({ authorId: 5, author: { + __typename: 'User' as const, email: 'five@5.com', fullName: 'Five Lastname', }, diff --git a/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.test.tsx b/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.test.tsx index 754c0e5746..9df5744f80 100644 --- a/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.test.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/BlogPostCell/BlogPostCell.test.tsx @@ -24,7 +24,7 @@ describe('BlogPostCell', () => { it('renders Failure successfully', async () => { expect(() => { - render() + render() }).not.toThrow() }) @@ -36,7 +36,7 @@ describe('BlogPostCell', () => { it('renders Success successfully', async () => { expect(() => { - render() + render() }).not.toThrow() }) }) diff --git a/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.mock.ts b/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.mock.ts index d75f304b92..b1569845f5 100644 --- a/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.mock.ts +++ b/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.mock.ts @@ -2,6 +2,7 @@ export const standard = (/* vars, { ctx, req } */) => ({ blogPosts: [ { + __typename: 'Post' as const, id: 42, title: 'Mocked title', body: 'Mocked body', @@ -9,11 +10,13 @@ export const standard = (/* vars, { ctx, req } */) => ({ authorId: 5, author: { + __typename: 'User' as const, email: 'five@5.com', fullName: 'Five Lastname', }, }, { + __typename: 'Post' as const, id: 43, title: 'Mocked title', body: 'Mocked body', @@ -21,11 +24,13 @@ export const standard = (/* vars, { ctx, req } */) => ({ authorId: 5, author: { + __typename: 'User' as const, email: 'five@5.com', fullName: 'Five Lastname', }, }, { + __typename: 'Post' as const, id: 44, title: 'Mocked title', body: 'Mocked body', @@ -33,6 +38,7 @@ export const standard = (/* vars, { ctx, req } */) => ({ authorId: 5, author: { + __typename: 'User' as const, email: 'five@5.com', fullName: 'Five Lastname', }, diff --git a/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.tsx b/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.tsx index 92c02fc7a3..3805fcc0cf 100644 --- a/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/BlogPostsCell/BlogPostsCell.tsx @@ -8,33 +8,35 @@ import type { import BlogPost from 'src/components/BlogPost' -export const QUERY: TypedDocumentNode< - BlogPostsQuery, - BlogPostsQueryVariables -> = gql` - query BlogPostsQuery { - blogPosts: posts { - id - title - body - author { - email - fullName +export const QUERY: TypedDocumentNode = + gql` + query BlogPostsQuery { + blogPosts: posts { + id + title + body + author { + email + fullName + } + createdAt } - createdAt } - } -` + ` export const Loading = () =>
Loading...
export const Empty = () =>
Empty
-export const Failure = ({ error }: CellFailureProps) => ( +export const Failure = ({ + error, +}: CellFailureProps) => (
Error: {error?.message}
) -export const Success = ({ blogPosts }: CellSuccessProps) => ( +export const Success = ({ + blogPosts, +}: CellSuccessProps) => (
{blogPosts.map((post) => ( diff --git a/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.stories.tsx b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.stories.tsx new file mode 100644 index 0000000000..b6855a5b07 --- /dev/null +++ b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.stories.tsx @@ -0,0 +1,26 @@ +// Pass props to your component by passing an `args` object to your story +// +// ```tsx +// export const Primary: Story = { +// args: { +// propName: propValue +// } +// } +// ``` +// +// See https://storybook.js.org/docs/7/writing-stories/args + +import type { Meta, StoryObj } from '@storybook/react' + +import ClassWithClassField from './ClassWithClassField' + +const meta: Meta = { + component: ClassWithClassField, + tags: ['autodocs'], +} + +export default meta + +type Story = StoryObj + +export const Primary: Story = {} diff --git a/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.test.tsx b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.test.tsx new file mode 100644 index 0000000000..0f342ba74b --- /dev/null +++ b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.test.tsx @@ -0,0 +1,14 @@ +import { render } from '@cedarjs/testing/web' + +import ClassWithClassField from './ClassWithClassField' + +// Improve this test with help from the Redwood Testing Doc: +// https://redwoodjs.com/docs/testing#testing-components + +describe('ClassWithClassField', () => { + it('renders successfully', () => { + expect(() => { + render() + }).not.toThrow() + }) +}) diff --git a/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.tsx b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.tsx new file mode 100644 index 0000000000..10cf41bad5 --- /dev/null +++ b/__fixtures__/fragment-test-project/web/src/components/ClassWithClassField/ClassWithClassField.tsx @@ -0,0 +1,12 @@ +class Bar {} + +class Foo { + // Without the correct babel plugins this will throw an error + public bar = new Bar() +} + +const ClassWithClassField = () => { + return

{new Foo().bar.toString()}

+} + +export default ClassWithClassField diff --git a/__fixtures__/fragment-test-project/web/src/components/Contact/Contact/Contact.tsx b/__fixtures__/fragment-test-project/web/src/components/Contact/Contact/Contact.tsx index 5e8ec77fa4..5fe428b4e7 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Contact/Contact/Contact.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Contact/Contact/Contact.tsx @@ -9,7 +9,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { timeTag } from 'src/lib/formatters' +import { timeTag } from 'src/lib/formatters.js' const DELETE_CONTACT_MUTATION: TypedDocumentNode< DeleteContactMutation, diff --git a/__fixtures__/fragment-test-project/web/src/components/Contact/Contacts/Contacts.tsx b/__fixtures__/fragment-test-project/web/src/components/Contact/Contacts/Contacts.tsx index e1fd1acf2d..fdc349093f 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Contact/Contacts/Contacts.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Contact/Contacts/Contacts.tsx @@ -10,7 +10,7 @@ import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' import { QUERY } from 'src/components/Contact/ContactsCell' -import { timeTag, truncate } from 'src/lib/formatters' +import { timeTag, truncate } from 'src/lib/formatters.js' const DELETE_CONTACT_MUTATION: TypedDocumentNode< DeleteContactMutation, diff --git a/__fixtures__/fragment-test-project/web/src/components/Contact/ContactsCell/ContactsCell.tsx b/__fixtures__/fragment-test-project/web/src/components/Contact/ContactsCell/ContactsCell.tsx index 525c2db599..0ed87869c5 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Contact/ContactsCell/ContactsCell.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Contact/ContactsCell/ContactsCell.tsx @@ -9,29 +9,27 @@ import type { import Contacts from 'src/components/Contact/Contacts' -export const QUERY: TypedDocumentNode< - FindContacts, - FindContactsVariables -> = gql` - query FindContacts { - contacts { - id - name - email - message - createdAt +export const QUERY: TypedDocumentNode = + gql` + query FindContacts { + contacts { + id + name + email + message + createdAt + } } - } -` + ` export const Loading = () =>
Loading...
export const Empty = () => { return (
- {'No contacts yet. '} + No contacts yet.{' '} - {'Create one?'} + Create one?
) diff --git a/__fixtures__/fragment-test-project/web/src/components/FruitInfo.tsx b/__fixtures__/fragment-test-project/web/src/components/FruitInfo.tsx index 02189836a1..a5dccaaa18 100644 --- a/__fixtures__/fragment-test-project/web/src/components/FruitInfo.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/FruitInfo.tsx @@ -5,19 +5,17 @@ import { registerFragment } from '@cedarjs/web/apollo' import Card from 'src/components/Card' import StallInfo from 'src/components/StallInfo' -const { useRegisteredFragment } = registerFragment( - gql` - fragment Fruit_info on Fruit { - id - name - isSeedless - ripenessIndicators - stall { - ...Stall_info - } +const { useRegisteredFragment } = registerFragment(gql` + fragment Fruit_info on Fruit { + id + name + isSeedless + ripenessIndicators + stall { + ...Stall_info } - ` -) + } +`) const FruitInfo = ({ id }: { id: string }) => { const { data: fruit, complete } = useRegisteredFragment(id) diff --git a/__fixtures__/fragment-test-project/web/src/components/Post/Post/Post.tsx b/__fixtures__/fragment-test-project/web/src/components/Post/Post/Post.tsx index 8b5f5429a8..74a1d83fa9 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Post/Post/Post.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Post/Post/Post.tsx @@ -9,7 +9,7 @@ import { useMutation } from '@cedarjs/web' import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' -import { timeTag } from 'src/lib/formatters' +import { timeTag } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, diff --git a/__fixtures__/fragment-test-project/web/src/components/Post/PostCell/PostCell.tsx b/__fixtures__/fragment-test-project/web/src/components/Post/PostCell/PostCell.tsx index 5bb33c70f7..a4b9ffb9db 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Post/PostCell/PostCell.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Post/PostCell/PostCell.tsx @@ -8,20 +8,18 @@ import type { import Post from 'src/components/Post/Post' -export const QUERY: TypedDocumentNode< - FindPostById, - FindPostByIdVariables -> = gql` - query FindPostById($id: Int!) { - post: post(id: $id) { - id - title - body - authorId - createdAt +export const QUERY: TypedDocumentNode = + gql` + query FindPostById($id: Int!) { + post: post(id: $id) { + id + title + body + authorId + createdAt + } } - } -` + ` export const Loading = () =>
Loading...
diff --git a/__fixtures__/fragment-test-project/web/src/components/Post/Posts/Posts.tsx b/__fixtures__/fragment-test-project/web/src/components/Post/Posts/Posts.tsx index 423cf4ae74..2c992a13e2 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Post/Posts/Posts.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Post/Posts/Posts.tsx @@ -10,7 +10,7 @@ import type { TypedDocumentNode } from '@cedarjs/web' import { toast } from '@cedarjs/web/toast' import { QUERY } from 'src/components/Post/PostsCell' -import { timeTag, truncate } from 'src/lib/formatters' +import { timeTag, truncate } from 'src/lib/formatters.js' const DELETE_POST_MUTATION: TypedDocumentNode< DeletePostMutation, diff --git a/__fixtures__/fragment-test-project/web/src/components/Post/PostsCell/PostsCell.tsx b/__fixtures__/fragment-test-project/web/src/components/Post/PostsCell/PostsCell.tsx index a69e6d6e92..e0bf9f130f 100644 --- a/__fixtures__/fragment-test-project/web/src/components/Post/PostsCell/PostsCell.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/Post/PostsCell/PostsCell.tsx @@ -26,9 +26,9 @@ export const Loading = () =>
Loading...
export const Empty = () => { return (
- {'No posts yet. '} + No posts yet.{' '} - {'Create one?'} + Create one?
) diff --git a/__fixtures__/fragment-test-project/web/src/components/ProduceInfo.tsx b/__fixtures__/fragment-test-project/web/src/components/ProduceInfo.tsx index 6f88f6b4df..9b08b6cf31 100644 --- a/__fixtures__/fragment-test-project/web/src/components/ProduceInfo.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/ProduceInfo.tsx @@ -4,14 +4,12 @@ import { registerFragment } from '@cedarjs/web/apollo' import Card from 'src/components/Card' -const { useRegisteredFragment } = registerFragment( - gql` - fragment Produce_info on Produce { - id - name - } - ` -) +const { useRegisteredFragment } = registerFragment(gql` + fragment Produce_info on Produce { + id + name + } +`) const ProduceInfo = ({ id }: { id: string }) => { const { data, complete } = useRegisteredFragment(id) diff --git a/__fixtures__/fragment-test-project/web/src/components/StallInfo.tsx b/__fixtures__/fragment-test-project/web/src/components/StallInfo.tsx index fe27b08729..3a10a20a8e 100644 --- a/__fixtures__/fragment-test-project/web/src/components/StallInfo.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/StallInfo.tsx @@ -2,14 +2,12 @@ import type { Stall } from 'types/graphql' import { registerFragment } from '@cedarjs/web/apollo' -const { useRegisteredFragment } = registerFragment( - gql` - fragment Stall_info on Stall { - id - name - } - ` -) +const { useRegisteredFragment } = registerFragment(gql` + fragment Stall_info on Stall { + id + name + } +`) const StallInfo = ({ id }: { id: string }) => { const { data, complete } = useRegisteredFragment(id) diff --git a/__fixtures__/fragment-test-project/web/src/components/VegetableInfo.tsx b/__fixtures__/fragment-test-project/web/src/components/VegetableInfo.tsx index 1fa75d7eec..9973e27192 100644 --- a/__fixtures__/fragment-test-project/web/src/components/VegetableInfo.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/VegetableInfo.tsx @@ -5,19 +5,17 @@ import { registerFragment } from '@cedarjs/web/apollo' import Card from 'src/components/Card' import StallInfo from 'src/components/StallInfo' -const { useRegisteredFragment } = registerFragment( - gql` - fragment Vegetable_info on Vegetable { - id - name - vegetableFamily - isPickled - stall { - ...Stall_info - } +const { useRegisteredFragment } = registerFragment(gql` + fragment Vegetable_info on Vegetable { + id + name + vegetableFamily + isPickled + stall { + ...Stall_info } - ` -) + } +`) const VegetableInfo = ({ id }: { id: string }) => { const { data: vegetable, complete } = useRegisteredFragment(id) diff --git a/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.mock.ts b/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.mock.ts index 55dd744ca5..cf02b60610 100644 --- a/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.mock.ts +++ b/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.mock.ts @@ -1,6 +1,7 @@ // Define your own mock data here: export const standard = (/* vars, { ctx, req } */) => ({ waterfallBlogPost: { + __typename: 'Post' as const, id: 42, title: 'Mocked title', body: 'Mocked body', @@ -8,6 +9,7 @@ export const standard = (/* vars, { ctx, req } */) => ({ authorId: 7, author: { + __typename: 'User' as const, email: 'se7en@7.com', fullName: 'Se7en Lastname', }, diff --git a/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.test.tsx b/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.test.tsx index 444f201b21..6701312f2c 100644 --- a/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.test.tsx +++ b/__fixtures__/fragment-test-project/web/src/components/WaterfallBlogPostCell/WaterfallBlogPostCell.test.tsx @@ -24,7 +24,7 @@ describe('WaterfallBlogPostCell', () => { it('renders Failure successfully', async () => { expect(() => { - render() + render() }).not.toThrow() }) @@ -36,7 +36,9 @@ describe('WaterfallBlogPostCell', () => { it('renders Success successfully', async () => { expect(() => { - render() + render( + + ) }).not.toThrow() }) }) diff --git a/__fixtures__/fragment-test-project/web/src/entry.client.tsx b/__fixtures__/fragment-test-project/web/src/entry.client.tsx index 0a9b18d319..915c14d76d 100644 --- a/__fixtures__/fragment-test-project/web/src/entry.client.tsx +++ b/__fixtures__/fragment-test-project/web/src/entry.client.tsx @@ -19,7 +19,8 @@ if (!redwoodAppElement) { } if (redwoodAppElement.children?.length > 0) { - hydrateRoot(redwoodAppElement, + hydrateRoot( + redwoodAppElement, diff --git a/__fixtures__/fragment-test-project/web/src/layouts/BlogLayout/BlogLayout.tsx b/__fixtures__/fragment-test-project/web/src/layouts/BlogLayout/BlogLayout.tsx index 8eb9275c65..87af741fa0 100644 --- a/__fixtures__/fragment-test-project/web/src/layouts/BlogLayout/BlogLayout.tsx +++ b/__fixtures__/fragment-test-project/web/src/layouts/BlogLayout/BlogLayout.tsx @@ -2,7 +2,7 @@ type BlogLayoutProps = { children?: React.ReactNode } -import { Link, routes } from '@cedarjs/router' +import { Link, NavLink, routes } from '@cedarjs/router' import { useAuth } from 'src/auth' @@ -23,48 +23,53 @@ const BlogLayout = ({ children }: BlogLayoutProps) => {
) -} +}; -export default GroceriesPage +export default GroceriesPage; diff --git a/__fixtures__/fragment-test-project/web/src/pages/HomePage/HomePage.tsx b/__fixtures__/fragment-test-project/web/src/pages/HomePage/HomePage.tsx index 6b7d185bc2..d86fe65702 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/HomePage/HomePage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/HomePage/HomePage.tsx @@ -1,6 +1,3 @@ -import { Link, routes } from '@cedarjs/router' -import { Metadata } from '@cedarjs/web' - import BlogPostsCell from 'src/components/BlogPostsCell' const HomePage = () => { diff --git a/__fixtures__/fragment-test-project/web/src/pages/LoginPage/LoginPage.tsx b/__fixtures__/fragment-test-project/web/src/pages/LoginPage/LoginPage.tsx index 665e142612..fced4f6181 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/LoginPage/LoginPage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/LoginPage/LoginPage.tsx @@ -1,5 +1,4 @@ -import { useRef } from 'react' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { Form, diff --git a/__fixtures__/fragment-test-project/web/src/pages/ProfilePage/ProfilePage.tsx b/__fixtures__/fragment-test-project/web/src/pages/ProfilePage/ProfilePage.tsx index 77cc5776a0..0209c00efe 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/ProfilePage/ProfilePage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/ProfilePage/ProfilePage.tsx @@ -1,7 +1,7 @@ -import { Link, routes } from '@cedarjs/router' import { Metadata } from '@cedarjs/web' import { useAuth } from 'src/auth' +// import { Link, routes } from '@cedarjs/router' const ProfilePage = () => { const { currentUser, isAuthenticated, hasRole, loading } = useAuth() diff --git a/__fixtures__/fragment-test-project/web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx b/__fixtures__/fragment-test-project/web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx index 89bd1f0870..fd45ae810f 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/ResetPasswordPage/ResetPasswordPage.tsx @@ -1,12 +1,6 @@ import { useEffect, useRef, useState } from 'react' -import { - Form, - Label, - PasswordField, - Submit, - FieldError, -} from '@cedarjs/forms' +import { Form, Label, PasswordField, Submit, FieldError } from '@cedarjs/forms' import { navigate, routes } from '@cedarjs/router' import { Metadata } from '@cedarjs/web' import { toast, Toaster } from '@cedarjs/web/toast' diff --git a/__fixtures__/fragment-test-project/web/src/pages/SignupPage/SignupPage.tsx b/__fixtures__/fragment-test-project/web/src/pages/SignupPage/SignupPage.tsx index fc02c8c331..8d4998b7e4 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/SignupPage/SignupPage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/SignupPage/SignupPage.tsx @@ -1,5 +1,4 @@ -import { useRef } from 'react' -import { useEffect } from 'react' +import { useEffect, useRef } from 'react' import { Form, diff --git a/__fixtures__/fragment-test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx b/__fixtures__/fragment-test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx index 6c4f24a14c..50926b0599 100644 --- a/__fixtures__/fragment-test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx +++ b/__fixtures__/fragment-test-project/web/src/pages/WaterfallPage/WaterfallPage.tsx @@ -7,5 +7,4 @@ type WaterfallPageProps = { const WaterfallPage = ({ id }: WaterfallPageProps) => ( ) - export default WaterfallPage diff --git a/__fixtures__/fragment-test-project/web/src/scaffold.css b/__fixtures__/fragment-test-project/web/src/scaffold.css index ffa9142b71..cfa7abf033 100644 --- a/__fixtures__/fragment-test-project/web/src/scaffold.css +++ b/__fixtures__/fragment-test-project/web/src/scaffold.css @@ -1,243 +1,398 @@ -.rw-scaffold { - @apply bg-white text-gray-600; +/* + normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css +*/ + +.rw-scaffold *, +.rw-scaffold ::after, +.rw-scaffold ::before { + box-sizing: inherit; +} +.rw-scaffold main { + color: #4a5568; + display: block; } .rw-scaffold h1, .rw-scaffold h2 { - @apply m-0; + margin: 0; } .rw-scaffold a { - @apply bg-transparent; + background-color: transparent; } .rw-scaffold ul { - @apply m-0 p-0; + margin: 0; + padding: 0; +} +.rw-scaffold input { + font-family: inherit; + font-size: 100%; + overflow: visible; } .rw-scaffold input:-ms-input-placeholder { - @apply text-gray-500; + color: #a0aec0; } .rw-scaffold input::-ms-input-placeholder { - @apply text-gray-500; + color: #a0aec0; } .rw-scaffold input::placeholder { - @apply text-gray-500; + color: #a0aec0; +} +.rw-scaffold table { + border-collapse: collapse; +} + +/* + Style +*/ + +.rw-scaffold, +.rw-toast { + background-color: #fff; + font-family: + -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", + Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", + "Segoe UI Symbol", "Noto Color Emoji"; } .rw-header { - @apply flex justify-between px-8 py-4; + display: flex; + justify-content: space-between; + padding: 1rem 2rem 1rem 2rem; } .rw-main { - @apply mx-4 pb-4; + margin-left: 1rem; + margin-right: 1rem; + padding-bottom: 1rem; } .rw-segment { - @apply w-full overflow-hidden rounded-lg border border-gray-200; - scrollbar-color: theme('colors.zinc.400') transparent; + border-radius: 0.5rem; + border-width: 1px; + border-color: #e5e7eb; + overflow: hidden; + width: 100%; + scrollbar-color: #a1a1aa transparent; } .rw-segment::-webkit-scrollbar { height: initial; } .rw-segment::-webkit-scrollbar-track { - @apply rounded-b-[10px] rounded-t-none border-0 border-t border-solid border-gray-200 bg-transparent p-[2px]; + background-color: transparent; + border-color: #e2e8f0; + border-style: solid; + border-radius: 0 0 10px 10px; + border-width: 1px 0 0 0; + padding: 2px; } .rw-segment::-webkit-scrollbar-thumb { - @apply rounded-full border-[3px] border-solid border-transparent bg-zinc-400 bg-clip-content; + background-color: #a1a1aa; + background-clip: content-box; + border: 3px solid transparent; + border-radius: 10px; } .rw-segment-header { - @apply bg-gray-200 px-4 py-3 text-gray-700; + background-color: #e2e8f0; + color: #4a5568; + padding: 0.75rem 1rem; } .rw-segment-main { - @apply bg-gray-100 p-4; + background-color: #f7fafc; + padding: 1rem; } .rw-link { - @apply text-blue-400 underline; + color: #4299e1; + text-decoration: underline; } .rw-link:hover { - @apply text-blue-500; + color: #2b6cb0; } .rw-forgot-link { - @apply mt-1 text-right text-xs text-gray-400 underline; + font-size: 0.75rem; + color: #a0aec0; + text-align: right; + margin-top: 0.1rem; } .rw-forgot-link:hover { - @apply text-blue-500; + font-size: 0.75rem; + color: #4299e1; } .rw-heading { - @apply font-semibold; + font-weight: 600; } .rw-heading.rw-heading-primary { - @apply text-xl; + font-size: 1.25rem; } .rw-heading.rw-heading-secondary { - @apply text-sm; + font-size: 0.875rem; } .rw-heading .rw-link { - @apply text-gray-600 no-underline; + color: #4a5568; + text-decoration: none; } .rw-heading .rw-link:hover { - @apply text-gray-900 underline; + color: #1a202c; + text-decoration: underline; } .rw-cell-error { - @apply text-sm font-semibold; + font-size: 90%; + font-weight: 600; } .rw-form-wrapper { - @apply -mt-4 text-sm; + box-sizing: border-box; + font-size: 0.875rem; + margin-top: -1rem; } .rw-cell-error, .rw-form-error-wrapper { - @apply my-4 rounded border border-red-100 bg-red-50 p-4 text-red-600; + padding: 1rem; + background-color: #fff5f5; + color: #c53030; + border-width: 1px; + border-color: #feb2b2; + border-radius: 0.25rem; + margin: 1rem 0; } .rw-form-error-title { - @apply m-0 font-semibold; + margin-top: 0; + margin-bottom: 0; + font-weight: 600; } .rw-form-error-list { - @apply mt-2 list-inside list-disc; + margin-top: 0.5rem; + list-style-type: disc; + list-style-position: inside; } .rw-button { - @apply flex cursor-pointer justify-center rounded border-0 bg-gray-200 px-4 py-1 text-xs font-semibold uppercase leading-loose tracking-wide text-gray-500 no-underline transition duration-100; + border: none; + color: #718096; + cursor: pointer; + display: flex; + justify-content: center; + font-size: 0.75rem; + font-weight: 600; + padding: 0.25rem 1rem; + text-transform: uppercase; + text-decoration: none; + letter-spacing: 0.025em; + border-radius: 0.25rem; + line-height: 2; + border: 0; } .rw-button:hover { - @apply bg-gray-500 text-white; + background-color: #718096; + color: #fff; } .rw-button.rw-button-small { - @apply rounded-sm px-2 py-1 text-xs; + font-size: 0.75rem; + border-radius: 0.125rem; + padding: 0.25rem 0.5rem; + line-height: inherit; } .rw-button.rw-button-green { - @apply bg-green-500 text-white; + background-color: #48bb78; + color: #fff; } .rw-button.rw-button-green:hover { - @apply bg-green-700; + background-color: #38a169; + color: #fff; } .rw-button.rw-button-blue { - @apply bg-blue-500 text-white; + background-color: #3182ce; + color: #fff; } .rw-button.rw-button-blue:hover { - @apply bg-blue-700; + background-color: #2b6cb0; } .rw-button.rw-button-red { - @apply bg-red-500 text-white; + background-color: #e53e3e; + color: #fff; } .rw-button.rw-button-red:hover { - @apply bg-red-700 text-white; + background-color: #c53030; } .rw-button-icon { - @apply mr-1 text-xl leading-5; + font-size: 1.25rem; + line-height: 1; + margin-right: 0.25rem; } .rw-button-group { - @apply mx-2 my-3 flex justify-center; + display: flex; + justify-content: center; + margin: 0.75rem 0.5rem; } .rw-button-group .rw-button { - @apply mx-1; + margin: 0 0.25rem; } .rw-form-wrapper .rw-button-group { - @apply mt-8; + margin-top: 2rem; + margin-bottom: 0; } .rw-label { - @apply mt-6 block text-left font-semibold text-gray-600; + display: block; + margin-top: 1.5rem; + color: #4a5568; + font-weight: 600; + text-align: left; } .rw-label.rw-label-error { - @apply text-red-600; + color: #c53030; } .rw-input { - @apply mt-2 block w-full rounded border border-gray-200 bg-white p-2 outline-none; -} -.rw-check-radio-items { - @apply flex justify-items-center; + display: block; + margin-top: 0.5rem; + width: 100%; + padding: 0.5rem; + border-width: 1px; + border-style: solid; + border-color: #e2e8f0; + color: #4a5568; + border-radius: 0.25rem; + outline: none; } .rw-check-radio-item-none { - @apply text-gray-600; + color: #4a5568; } -.rw-input[type='checkbox'], -.rw-input[type='radio'] { - @apply ml-0 mr-1 mt-1 inline w-4; +.rw-check-radio-items { + display: flex; + justify-items: center; +} +.rw-input[type="checkbox"] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; +} +.rw-input[type="radio"] { + display: inline; + width: 1rem; + margin-left: 0; + margin-right: 0.5rem; + margin-top: 0.25rem; } .rw-input:focus { - @apply border-gray-400; + border-color: #a0aec0; } .rw-input-error { - @apply border-red-600 text-red-600; + border-color: #c53030; + color: #c53030; } + .rw-input-error:focus { - @apply border-red-600 outline-none; + outline: none; + border-color: #c53030; box-shadow: 0 0 5px #c53030; } + .rw-field-error { - @apply mt-1 block text-xs font-semibold uppercase text-red-600; + display: block; + margin-top: 0.25rem; + font-weight: 600; + text-transform: uppercase; + font-size: 0.75rem; + color: #c53030; } .rw-table-wrapper-responsive { - @apply overflow-x-auto; + overflow-x: auto; } .rw-table-wrapper-responsive .rw-table { min-width: 48rem; } .rw-table { - @apply w-full text-sm; + table-layout: auto; + width: 100%; + font-size: 0.875rem; } .rw-table th, .rw-table td { - @apply p-3; + padding: 0.75rem; } .rw-table td { - @apply bg-white text-gray-900; + background-color: #ffffff; + color: #1a202c; } .rw-table tr:nth-child(odd) td, .rw-table tr:nth-child(odd) th { - @apply bg-gray-50; + background-color: #f7fafc; } .rw-table thead tr { - @apply bg-gray-200 text-gray-600; + color: #4a5568; } .rw-table th { - @apply text-left font-semibold; + font-weight: 600; + text-align: left; } .rw-table thead th { - @apply text-left; + background-color: #e2e8f0; + text-align: left; } .rw-table tbody th { - @apply text-right; + text-align: right; } @media (min-width: 768px) { .rw-table tbody th { - @apply w-1/5; + width: 20%; } } .rw-table tbody tr { - @apply border-t border-gray-200; + border-top-width: 1px; } .rw-table input { - @apply ml-0; + margin-left: 0; } .rw-table-actions { - @apply flex h-4 items-center justify-end pr-1; + display: flex; + justify-content: flex-end; + align-items: center; + height: 17px; + padding-right: 0.25rem; } .rw-table-actions .rw-button { - @apply bg-transparent; + background-color: transparent; } .rw-table-actions .rw-button:hover { - @apply bg-gray-500 text-white; + background-color: #718096; + color: #fff; } .rw-table-actions .rw-button-blue { - @apply text-blue-500; + color: #3182ce; } .rw-table-actions .rw-button-blue:hover { - @apply bg-blue-500 text-white; + background-color: #3182ce; + color: #fff; } .rw-table-actions .rw-button-red { - @apply text-red-600; + color: #e53e3e; } .rw-table-actions .rw-button-red:hover { - @apply bg-red-600 text-white; + background-color: #e53e3e; + color: #fff; } .rw-text-center { - @apply text-center; + text-align: center; } .rw-login-container { - @apply mx-auto my-16 flex w-96 flex-wrap items-center justify-center; + display: flex; + align-items: center; + justify-content: center; + width: 24rem; + margin: 4rem auto; + flex-wrap: wrap; } .rw-login-container .rw-form-wrapper { - @apply w-full text-center; + width: 100%; + text-align: center; } .rw-login-link { - @apply mt-4 w-full text-center text-sm text-gray-600; + margin-top: 1rem; + color: #4a5568; + font-size: 90%; + text-align: center; + flex-basis: 100%; } .rw-webauthn-wrapper { - @apply mx-4 mt-6 leading-6; + margin: 1.5rem 1rem 1rem; + line-height: 1.4; } .rw-webauthn-wrapper h2 { - @apply mb-4 text-xl font-bold; + font-size: 150%; + font-weight: bold; + margin-bottom: 1rem; } diff --git a/__fixtures__/fragment-test-project/web/tsconfig.json b/__fixtures__/fragment-test-project/web/tsconfig.json index a197b89fbf..edfcaff396 100644 --- a/__fixtures__/fragment-test-project/web/tsconfig.json +++ b/__fixtures__/fragment-test-project/web/tsconfig.json @@ -6,7 +6,6 @@ "target": "ES2022", "module": "ESNext", "moduleResolution": "Bundler", - "baseUrl": "./", "skipLibCheck": false, "rootDirs": [ "./src", @@ -21,17 +20,22 @@ "../api/src/*", "../.redwood/types/mirror/api/src/*" ], - "$api/*": [ "../api/*" ], + "$api/*": ["../api/*"], "types/*": ["./types/*", "../types/*"], "@cedarjs/testing": ["../node_modules/@cedarjs/testing/web"] }, - "typeRoots": ["../node_modules/@types", "./node_modules/@types", "../node_modules/@testing-library"], + "typeRoots": [ + "../node_modules/@types", + "./node_modules/@types", + "../node_modules/@testing-library" + ], "types": ["jest", "jest-dom"], "jsx": "preserve" }, "include": [ "src", "config", + ".storybook/**/*", "../.redwood/types/includes/all-*", "../.redwood/types/includes/web-*", "../types", diff --git a/__fixtures__/fragment-test-project/web/types/graphql.d.ts b/__fixtures__/fragment-test-project/web/types/graphql.d.ts index 382f3efb64..a8f099050b 100644 --- a/__fixtures__/fragment-test-project/web/types/graphql.d.ts +++ b/__fixtures__/fragment-test-project/web/types/graphql.d.ts @@ -12,8 +12,10 @@ export type Scalars = { Int: number; Float: number; BigInt: number; + Byte: Buffer; Date: string; DateTime: string; + File: File; JSON: Prisma.JsonValue; JSONObject: Prisma.JsonObject; Time: string; @@ -200,6 +202,8 @@ export type Produce = { /** About the Redwood queries. */ export type Query = { __typename?: 'Query'; + /** Fetches the CedarJS root schema. */ + cedarjs?: Maybe; contact?: Maybe; contacts: Array; fruitById?: Maybe; @@ -261,9 +265,9 @@ export type QueryvegetableByIdArgs = { }; /** - * The RedwoodJS Root Schema + * The Cedar Root Schema * - * Defines details about RedwoodJS such as the current user and version information. + * Defines details about Cedar such as the current user and version information. */ export type Redwood = { __typename?: 'Redwood'; @@ -271,7 +275,7 @@ export type Redwood = { currentUser?: Maybe; /** The version of Prisma. */ prismaVersion?: Maybe; - /** The version of Redwood. */ + /** The version of CedarJS. */ version?: Maybe; }; diff --git a/__fixtures__/fragment-test-project/web/vite.config.ts b/__fixtures__/fragment-test-project/web/vite.config.ts index 19bb2d5793..8db6f5be72 100644 --- a/__fixtures__/fragment-test-project/web/vite.config.ts +++ b/__fixtures__/fragment-test-project/web/vite.config.ts @@ -1,4 +1,6 @@ -import dns from 'dns' +/// + +import dns from 'node:dns' import { defineConfig } from 'vite' @@ -8,6 +10,12 @@ import { cedar } from '@cedarjs/vite' // See: https://vitejs.dev/config/server-options.html#server-host. dns.setDefaultResultOrder('verbatim') -export default defineConfig({ - plugins: [cedar()], -}) +export default defineConfig(({ mode }) => ({ + plugins: [cedar({ mode })], + test: { + environment: 'jsdom', + setupFiles: ['./vitest.setup.ts'], + // Enables global test APIs like describe, it, expect + globals: true, + }, +})) diff --git a/__fixtures__/fragment-test-project/web/vitest.setup.ts b/__fixtures__/fragment-test-project/web/vitest.setup.ts new file mode 100644 index 0000000000..3c2a688f47 --- /dev/null +++ b/__fixtures__/fragment-test-project/web/vitest.setup.ts @@ -0,0 +1,12 @@ +import '@testing-library/jest-dom/vitest' + +import { cleanup } from '@testing-library/react' +import { afterEach } from 'vitest' + +afterEach(() => { + // If vitest globals are enabled testing-library will clean up after each + // test automatically, but we don't enable globals, so we have to manually + // clean up here + // https://testing-library.com/docs/react-testing-library/api/#cleanup + cleanup() +}) From 85c157da963b344dab90066ed7f2114008825d45 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 13:57:22 +0200 Subject: [PATCH 093/222] chore(ci): Remove unneeded awaits in specs --- tasks/smoke-tests/auth/tests/authChecks.spec.ts | 10 ++++------ tasks/smoke-tests/auth/tests/rbacChecks.spec.ts | 6 +++--- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tasks/smoke-tests/auth/tests/authChecks.spec.ts b/tasks/smoke-tests/auth/tests/authChecks.spec.ts index 2472db0427..5b69136939 100644 --- a/tasks/smoke-tests/auth/tests/authChecks.spec.ts +++ b/tasks/smoke-tests/auth/tests/authChecks.spec.ts @@ -29,21 +29,19 @@ test('useAuth hook, auth redirects checks', async ({ page }) => { await page.goto('/profile') const usernameRow = await page.waitForSelector('*css=tr >> text=EMAIL') - await expect(await usernameRow.innerHTML()).toBe( + expect(await usernameRow.innerHTML()).toBe( 'EMAILtestuser@bazinga.com', ) const isAuthenticatedRow = await page.waitForSelector( '*css=tr >> text=isAuthenticated', ) - await expect(await isAuthenticatedRow.innerHTML()).toBe( + expect(await isAuthenticatedRow.innerHTML()).toBe( 'isAuthenticatedtrue', ) const isAdminRow = await page.waitForSelector('*css=tr >> text=Is Admin') - await expect(await isAdminRow.innerHTML()).toBe( - 'Is Adminfalse', - ) + expect(await isAdminRow.innerHTML()).toBe('Is Adminfalse') await page.goto('/') await page.getByText('Log Out').click() @@ -60,7 +58,7 @@ test('requireAuth graphql checks', async ({ page }) => { // Try to create a post as an anonymous user. await createNewPost({ page }) - await expect( + expect( page .locator('.rw-form-error-title') .locator("text=You don't have permission to do that"), diff --git a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts index fabceace9a..dd2db3b28a 100644 --- a/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts +++ b/tasks/smoke-tests/auth/tests/rbacChecks.spec.ts @@ -65,7 +65,7 @@ test('RBAC: Should not be able to delete contact as non-admin user', async ({ await page.locator('text=Delete').first().click() - await expect( + expect( page .locator('.rw-scaffold') .locator("text=You don't have permission to do that"), @@ -77,7 +77,7 @@ test('RBAC: Should not be able to delete contact as non-admin user', async ({ page.locator('.rw-scaffold').locator('text=Contact deleted'), ).toBeHidden() - await expect( + expect( await page.locator('text=charlie@chimichanga.com').count(), ).toBeGreaterThan(0) }) @@ -137,7 +137,7 @@ export default async ({ args }) => { page.locator('.rw-scaffold').locator('text=Contact deleted'), ).toBeVisible() - await expect(await page.locator('text=charlie@chimichanga.com').count()).toBe( + expect(await page.locator('text=charlie@chimichanga.com').count()).toBe( contactCountBefore - 1, ) }) From 0c7562444ff607c89325946973bcd256f9aca944 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 14:00:08 +0200 Subject: [PATCH 094/222] debug tests --- packages/cli/src/commands/__tests__/test.test.js | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/commands/__tests__/test.test.js b/packages/cli/src/commands/__tests__/test.test.js index d4eba523d9..d7e3a6a2b6 100644 --- a/packages/cli/src/commands/__tests__/test.test.js +++ b/packages/cli/src/commands/__tests__/test.test.js @@ -1,5 +1,5 @@ globalThis.__dirname = __dirname -import '../../lib/test' +import '../../lib/test.js' vi.mock('execa', () => ({ default: vi.fn((cmd, params) => ({ @@ -21,17 +21,6 @@ vi.mock('@cedarjs/structure', () => { } }) -// Before rw tests run, api/ and web/ `vitest.config.ts` is confirmed via existsSync() -// vi.mock('fs-extra', async (importOriginal) => { -// const originalFsExtra = await importOriginal() -// return { -// default: { -// ...originalFsExtra, -// existsSync: () => true, -// }, -// } -// }) - afterEach(() => { vi.clearAllMocks() }) @@ -39,6 +28,9 @@ afterEach(() => { test('Runs tests for all available sides if no filter passed', async () => { await handler({}) + console.log('execa', execa) + console.log('execa.mock.results', execa.mock.results) + expect(execa.mock.results[0].value.cmd).toBe('yarn vitest') expect(execa.mock.results[0].value.params).toContain('web') expect(execa.mock.results[0].value.params).toContain('api') From b2d24fb97b93b256de0c9d20fc967655490aa535 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 14:32:06 +0200 Subject: [PATCH 095/222] fix tests on CI --- packages/cli/src/commands/testHandler.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 63df94d298..8846bac40b 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -12,6 +12,7 @@ export const handler = async ({ dbPush = true, ...others }) => { + console.log('testHandler.js enter') recordTelemetryAttributes({ command: 'test', dbPush, @@ -62,9 +63,8 @@ export const handler = async ({ ].filter((flagOrValue) => flagOrValue !== null) // Filter out nulls, not booleans because user may have passed a --something false flag if (process.env.CI) { - // Don't run in watch mode in CI + // Force run mode in CI vitestArgs.push('--run') - watch = false } // if no sides declared with yargs, default to all sides @@ -102,6 +102,7 @@ export const handler = async ({ if (watch) { await runCommand() } else { + console.log('testHandler.js using timedTelemetry') await timedTelemetry(process.argv, { type: 'test' }, async () => { await runCommand() }) From c1eb936a91ee1d3e73f6b4183bec1a4f372bdeda Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Wed, 23 Jul 2025 14:51:29 +0200 Subject: [PATCH 096/222] update test-project templates to use .js on relative imports --- .../templates/api/contacts.describeScenario.test.ts.template | 4 ++-- tasks/test-project/templates/api/groceries.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template b/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template index c4a8e95e9e..88273f5d1a 100644 --- a/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template +++ b/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template @@ -4,8 +4,8 @@ import { contact, contacts, createContact -} from './contacts' -import type { StandardScenario } from './contacts.scenarios' +} from './contacts.js' +import type { StandardScenario } from './contacts.scenarios.js' /** * Example test for describe scenario. diff --git a/tasks/test-project/templates/api/groceries.ts b/tasks/test-project/templates/api/groceries.ts index 09eb5de330..834b39eea5 100644 --- a/tasks/test-project/templates/api/groceries.ts +++ b/tasks/test-project/templates/api/groceries.ts @@ -1,6 +1,6 @@ -import { Produce } from 'types/graphql' +import type { Produce } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' const isFruit = (grocery: Produce) => { return grocery.isSeedless !== null && grocery.ripenessIndicators !== null From 86cac469e645a2dae1450d5b06d296237fcc4936 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 12:53:39 +0200 Subject: [PATCH 097/222] fix(prerender): Use built LocationProvider --- .../src/build-and-import/buildAndImport.ts | 7 ---- packages/prerender/src/runPrerender.tsx | 32 +++++++++++++------ 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/packages/prerender/src/build-and-import/buildAndImport.ts b/packages/prerender/src/build-and-import/buildAndImport.ts index 011eefcd10..ccb8dc7044 100644 --- a/packages/prerender/src/build-and-import/buildAndImport.ts +++ b/packages/prerender/src/build-and-import/buildAndImport.ts @@ -49,15 +49,8 @@ export async function buildAndImport( throw new Error(`${options.filepath} is not a valid JS file`) } - console.log('options', options) - const tsConfigs = parseTypeScriptConfigFiles() - console.log( - 'tsConfigs.web?.data.compilerOptions', - tsConfigs.web?.compilerOptions, - ) - const resolvePaths = tsconfigPathsToRegExp( tsConfigs.web?.compilerOptions?.paths || {}, ) diff --git a/packages/prerender/src/runPrerender.tsx b/packages/prerender/src/runPrerender.tsx index 597d518aec..9faf3c7598 100644 --- a/packages/prerender/src/runPrerender.tsx +++ b/packages/prerender/src/runPrerender.tsx @@ -2,6 +2,7 @@ import fs from 'node:fs' import path from 'node:path' import React from 'react' +import type { ElementType, FunctionComponent } from 'react' // @ts-expect-error CJS vs ESM import { ApolloClient, InMemoryCache } from '@apollo/client' @@ -14,7 +15,6 @@ import { ensurePosixPath, importStatementPath, } from '@cedarjs/project-config' -import { LocationProvider } from '@cedarjs/router' import { matchPath } from '@cedarjs/router/dist/util' import type { QueryInfo } from '@cedarjs/web' @@ -32,9 +32,10 @@ import { getRootHtmlPath, registerShims, writeToDist } from './internal' const prerenderApolloClient = new ApolloClient({ cache: new InMemoryCache() }) async function recursivelyRender( - App: React.ElementType, - Routes: React.ElementType, - CellCacheContextProvider: React.ElementType, + App: ElementType, + Routes: ElementType, + CellCacheContextProvider: ElementType, + LocationProvider: ElementType, renderPath: string, gqlHandler: any, queryCache: Record, @@ -141,6 +142,7 @@ async function recursivelyRender( App, Routes, CellCacheContextProvider, + LocationProvider, renderPath, gqlHandler, queryCache, @@ -245,11 +247,13 @@ interface Args { async function createCombinedEntry({ appPath, routesPath, outDir }: Args) { const combinedContent = ` + import { LocationProvider } from '@cedarjs/router' + import { CellCacheContextProvider } from '@cedarjs/web' + import App from "${importStatementPath(appPath.replace('.tsx', ''))}"; import Routes from "${importStatementPath(routesPath.replace('.tsx', ''))}"; - import { CellCacheContextProvider } from '@cedarjs/web' - export { App, Routes, CellCacheContextProvider }; + export { LocationProvider, CellCacheContextProvider, App, Routes }; ` const tempFilePath = path.join(outDir, '__prerender-temp-entry.tsx') @@ -258,9 +262,10 @@ async function createCombinedEntry({ appPath, routesPath, outDir }: Args) { } const renderCache: { - App?: React.FunctionComponent - Routes?: React.FunctionComponent - CellCacheContextProvider?: React.FunctionComponent + App?: FunctionComponent + Routes?: FunctionComponent + CellCacheContextProvider?: FunctionComponent + LocationProvider?: FunctionComponent } = {} interface PrerenderParams { @@ -294,9 +299,11 @@ export const runPrerender = async ({ renderCache.App = required.App renderCache.Routes = required.Routes renderCache.CellCacheContextProvider = required.CellCacheContextProvider + renderCache.LocationProvider = required.LocationProvider } - const { App, Routes, CellCacheContextProvider } = renderCache + const { LocationProvider, App, Routes, CellCacheContextProvider } = + renderCache if (!App) { throw new Error('App not found') @@ -310,10 +317,15 @@ export const runPrerender = async ({ throw new Error('CellCacheContextProvider not found') } + if (!LocationProvider) { + throw new Error('LocationProvider not found') + } + const componentAsHtml = await recursivelyRender( App, Routes, CellCacheContextProvider, + LocationProvider, renderPath, gqlHandler, queryCache, From b020da4950e019794e986307c7af5784326f4386 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 13:27:25 +0200 Subject: [PATCH 098/222] update templates to include vitest as a dev dep --- __fixtures__/test-project/package.json | 5 +++-- packages/create-cedar-app/templates/js/package.json | 3 ++- packages/create-cedar-app/templates/ts/package.json | 3 ++- packages/testing/package.json | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index a25bd5d72a..224d1e5171 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -11,7 +11,8 @@ "@cedarjs/core": "0.0.5", "@cedarjs/project-config": "0.0.5", "@cedarjs/testing": "0.0.5", - "prettier-plugin-tailwindcss": "^0.5.12" + "prettier-plugin-tailwindcss": "^0.5.12", + "vitest": "3.2.4" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", @@ -29,4 +30,4 @@ "react-is": "19.0.0-rc-f2df5694-20240916", "vite": "5.4.16" } -} \ No newline at end of file +} diff --git a/packages/create-cedar-app/templates/js/package.json b/packages/create-cedar-app/templates/js/package.json index c890d69f0e..e403c327ae 100644 --- a/packages/create-cedar-app/templates/js/package.json +++ b/packages/create-cedar-app/templates/js/package.json @@ -10,7 +10,8 @@ "devDependencies": { "@cedarjs/core": "0.0.5", "@cedarjs/project-config": "0.0.5", - "@cedarjs/testing": "0.0.5" + "@cedarjs/testing": "0.0.5", + "vitest": "3.2.4" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", diff --git a/packages/create-cedar-app/templates/ts/package.json b/packages/create-cedar-app/templates/ts/package.json index c890d69f0e..e403c327ae 100644 --- a/packages/create-cedar-app/templates/ts/package.json +++ b/packages/create-cedar-app/templates/ts/package.json @@ -10,7 +10,8 @@ "devDependencies": { "@cedarjs/core": "0.0.5", "@cedarjs/project-config": "0.0.5", - "@cedarjs/testing": "0.0.5" + "@cedarjs/testing": "0.0.5", + "vitest": "3.2.4" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", diff --git a/packages/testing/package.json b/packages/testing/package.json index 5a78af81f1..f3987f98a9 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -143,6 +143,7 @@ "msw": "1.3.4", "ts-toolbelt": "9.6.0", "unplugin-auto-import": "19.3.0", + "vitest": "3.2.4", "whatwg-fetch": "3.6.20" }, "devDependencies": { @@ -151,8 +152,7 @@ "jsdom": "24.1.3", "publint": "0.3.12", "tsx": "4.20.3", - "typescript": "5.6.2", - "vitest": "3.2.4" + "typescript": "5.6.2" }, "peerDependencies": { "vitest": "3.2.4" From 42cf79a6de1b8d57cf7babdfcb2af3f22dda4613 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 13:44:19 +0200 Subject: [PATCH 099/222] update default vitest config --- __fixtures__/test-project/vitest.config.ts | 2 +- packages/create-cedar-app/templates/js/vitest.config.mjs | 2 +- packages/create-cedar-app/templates/ts/vitest.config.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/__fixtures__/test-project/vitest.config.ts b/__fixtures__/test-project/vitest.config.ts index 79b00e32e9..bebad23e65 100644 --- a/__fixtures__/test-project/vitest.config.ts +++ b/__fixtures__/test-project/vitest.config.ts @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - projects: ['/{*,!(node_modules)/**/}/vite*'], + projects: ['/{*,!(node_modules)/**/}/vite*.config.*'], }, }) diff --git a/packages/create-cedar-app/templates/js/vitest.config.mjs b/packages/create-cedar-app/templates/js/vitest.config.mjs index 79b00e32e9..bebad23e65 100644 --- a/packages/create-cedar-app/templates/js/vitest.config.mjs +++ b/packages/create-cedar-app/templates/js/vitest.config.mjs @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - projects: ['/{*,!(node_modules)/**/}/vite*'], + projects: ['/{*,!(node_modules)/**/}/vite*.config.*'], }, }) diff --git a/packages/create-cedar-app/templates/ts/vitest.config.ts b/packages/create-cedar-app/templates/ts/vitest.config.ts index 79b00e32e9..bebad23e65 100644 --- a/packages/create-cedar-app/templates/ts/vitest.config.ts +++ b/packages/create-cedar-app/templates/ts/vitest.config.ts @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - projects: ['/{*,!(node_modules)/**/}/vite*'], + projects: ['/{*,!(node_modules)/**/}/vite*.config.*'], }, }) From b95567db9150d00c3d2742fb1e3f4c800572612d Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 13:44:57 +0200 Subject: [PATCH 100/222] remove debug logs --- packages/cli/src/commands/testHandler.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/cli/src/commands/testHandler.js b/packages/cli/src/commands/testHandler.js index 8846bac40b..1dddbbc9cb 100644 --- a/packages/cli/src/commands/testHandler.js +++ b/packages/cli/src/commands/testHandler.js @@ -12,7 +12,6 @@ export const handler = async ({ dbPush = true, ...others }) => { - console.log('testHandler.js enter') recordTelemetryAttributes({ command: 'test', dbPush, @@ -102,7 +101,6 @@ export const handler = async ({ if (watch) { await runCommand() } else { - console.log('testHandler.js using timedTelemetry') await timedTelemetry(process.argv, { type: 'test' }, async () => { await runCommand() }) From 059375989fac5fcc5a3a8d44aa6c0e3442d27731 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 13:49:42 +0200 Subject: [PATCH 101/222] chore(prerender): Use readFile instead of require for json file --- .../rollup-plugin-cedarjs-prerender-media-imports.ts | 4 ++-- packages/prerender/src/graphql/graphql.ts | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-prerender-media-imports.ts b/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-prerender-media-imports.ts index 7762d9c370..8c431e78d1 100644 --- a/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-prerender-media-imports.ts +++ b/packages/prerender/src/build-and-import/rollupPlugins/rollup-plugin-cedarjs-prerender-media-imports.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs' import { extname, join, relative, dirname } from 'node:path' import type { Plugin } from 'rollup' @@ -66,8 +67,7 @@ export function cedarjsPrerenderMediaImportsPlugin( getPaths().web.dist, 'client-build-manifest.json', ) - delete require.cache[require.resolve(manifestPath)] - buildManifest = require(manifestPath) + buildManifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8')) } catch { // Manifest not found, all imports will fallback to data URLs buildManifest = {} diff --git a/packages/prerender/src/graphql/graphql.ts b/packages/prerender/src/graphql/graphql.ts index 27d9b1f68d..50c02f46ff 100644 --- a/packages/prerender/src/graphql/graphql.ts +++ b/packages/prerender/src/graphql/graphql.ts @@ -1,3 +1,4 @@ +import fs from 'node:fs/promises' import path from 'path' import type { DocumentNode } from 'graphql' @@ -45,7 +46,8 @@ export async function executeQuery( // generated on the web side) if (config.graphql.trustedDocuments) { const documentsPath = path.join(getPaths().web.graphql, 'graphql') - const documents: Record | undefined = require(documentsPath) + const documentsText = await fs.readFile(documentsPath, 'utf8') + const documents = JSON.parse(documentsText) const documentName = operationName[0].toUpperCase() + operationName.slice(1) + 'Document' const queryHash = documents?.[documentName]?.__meta__?.hash From 66f3ed556dd5ef8d18558709f54a2841e18092be Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Thu, 24 Jul 2025 14:30:44 +0200 Subject: [PATCH 102/222] chore(cli): Remove unused runScriptFunction() from execBabel --- packages/cli/src/lib/execBabel.js | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/packages/cli/src/lib/execBabel.js b/packages/cli/src/lib/execBabel.js index 86081bd224..a2d9a1f45e 100644 --- a/packages/cli/src/lib/execBabel.js +++ b/packages/cli/src/lib/execBabel.js @@ -1,32 +1,9 @@ -import { createRequire } from 'node:module' -import path from 'node:path' - import { getWebSideDefaultBabelConfig, registerApiSideBabelHook, } from '@cedarjs/babel-config' import { getPaths } from '@cedarjs/project-config' -// This function is used both by the "exec" and "prerender" commands -export async function runScriptFunction({ - path: scriptPath, - functionName, - args, -}) { - const createdRequire = createRequire(import.meta.url) - const script = createdRequire(scriptPath) - const returnValue = await script[functionName](args) - - try { - const { db } = createdRequire(path.join(getPaths().api.lib, 'db')) - db.$disconnect() - } catch (e) { - // silence - } - - return returnValue -} - export function configureBabel() { const { overrides: _overrides, From 70cbbf7b97d5b61a789de4c8601aa237189ded32 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 25 Jul 2025 23:10:58 +0200 Subject: [PATCH 103/222] wip prerender esm support --- packages/prerender/src/graphql/graphql.ts | 1 + packages/prerender/src/graphql/node-runner.ts | 46 +++++++++---------- .../graphql/vite-plugin-cedar-import-dir.ts | 2 +- packages/prerender/src/runPrerender.tsx | 4 +- 4 files changed, 25 insertions(+), 28 deletions(-) diff --git a/packages/prerender/src/graphql/graphql.ts b/packages/prerender/src/graphql/graphql.ts index 0172253226..01a164bc27 100644 --- a/packages/prerender/src/graphql/graphql.ts +++ b/packages/prerender/src/graphql/graphql.ts @@ -1,5 +1,6 @@ import path from 'path' +import type { DocumentNode } from 'graphql' import { print } from 'graphql' import { getConfig, getPaths } from '@cedarjs/project-config' diff --git a/packages/prerender/src/graphql/node-runner.ts b/packages/prerender/src/graphql/node-runner.ts index ccdcb101a0..9fa9e30f90 100644 --- a/packages/prerender/src/graphql/node-runner.ts +++ b/packages/prerender/src/graphql/node-runner.ts @@ -4,11 +4,7 @@ import { ViteNodeRunner } from 'vite-node/client' import { ViteNodeServer } from 'vite-node/server' import { installSourcemapsSupport } from 'vite-node/source-map' -import { - getPaths, - importStatementPath, - projectIsEsm, -} from '@cedarjs/project-config' +import { getPaths, projectIsEsm } from '@cedarjs/project-config' import { cedarCellTransform, cedarjsDirectoryNamedImportPlugin, @@ -47,29 +43,29 @@ async function createViteServer() { }, { find: /^src\//, - replacement: 'src/', - customResolver: (id, importer, _options) => { - const apiImportBase = importStatementPath(getPaths().api.base) - const webImportBase = importStatementPath(getPaths().web.base) + replacement: getPaths().api.src + '/', + // customResolver: (id, importer, _options) => { + // const apiImportBase = importStatementPath(getPaths().api.base) + // const webImportBase = importStatementPath(getPaths().web.base) - let resolved: { id: string } | null = null + // let resolved: { id: string } | null = null - // When importing a file from the api directory (using api/src/... - // in the script), that file in turn might import another file using - // just src/... That's a problem for Vite when it's running a file - // from scripts/ because it doesn't know what the src/ alias is. - // So we have to tell it to use the correct path based on what file - // is doing the importing. - if (importer?.startsWith(apiImportBase)) { - const apiImportSrc = importStatementPath(getPaths().api.src) - resolved = { id: id.replace('src', apiImportSrc) } - } else if (importer?.startsWith(webImportBase)) { - const webImportSrc = importStatementPath(getPaths().web.src) - resolved = { id: id.replace('src', webImportSrc) } - } + // // When importing a file from the api directory (using api/src/... + // // in the script), that file in turn might import another file using + // // just src/... That's a problem for Vite when it's running a file + // // from scripts/ because it doesn't know what the src/ alias is. + // // So we have to tell it to use the correct path based on what file + // // is doing the importing. + // if (importer?.startsWith(apiImportBase)) { + // const apiImportSrc = importStatementPath(getPaths().api.src) + // resolved = { id: id.replace('src', apiImportSrc) } + // } else if (importer?.startsWith(webImportBase)) { + // const webImportSrc = importStatementPath(getPaths().web.src) + // resolved = { id: id.replace('src', webImportSrc) } + // } - return resolved - }, + // return resolved + // }, }, ], }, diff --git a/packages/prerender/src/graphql/vite-plugin-cedar-import-dir.ts b/packages/prerender/src/graphql/vite-plugin-cedar-import-dir.ts index fbd57be071..129dd68309 100644 --- a/packages/prerender/src/graphql/vite-plugin-cedar-import-dir.ts +++ b/packages/prerender/src/graphql/vite-plugin-cedar-import-dir.ts @@ -155,7 +155,7 @@ export function cedarImportDirPlugin( // Create namespace import: import * as importName_filePathVarName from 'filepath' const finalImportPath = projectIsEsm - ? `${filePathWithoutExtension}.js` + ? `${filePathWithoutExtension}` : filePathWithoutExtension newBody.push({ diff --git a/packages/prerender/src/runPrerender.tsx b/packages/prerender/src/runPrerender.tsx index 18614c6248..355d93f575 100644 --- a/packages/prerender/src/runPrerender.tsx +++ b/packages/prerender/src/runPrerender.tsx @@ -4,7 +4,7 @@ import path from 'node:path' import React from 'react' import type { ElementType, FunctionComponent } from 'react' -import * as pkg from '@apollo/client' +import * as apolloClient from '@apollo/client' import type { CheerioAPI } from 'cheerio' import { load as loadHtml } from 'cheerio' import ReactDOMServer from 'react-dom/server' @@ -29,7 +29,7 @@ import { NodeRunner } from './graphql/node-runner.js' import { getRootHtmlPath, registerShims, writeToDist } from './internal.js' // @ts-expect-error - ESM/CJS issue -const { ApolloClient, InMemoryCache } = pkg.default +const { ApolloClient, InMemoryCache } = apolloClient.default // Create an apollo client that we can use to prepopulate the cache and restore it client-side const prerenderApolloClient = new ApolloClient({ cache: new InMemoryCache() }) From 24b7efd2a96360aa2e995beb114a50ea687f1d0f Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 25 Jul 2025 23:37:29 +0200 Subject: [PATCH 104/222] skip test --- .../vite-plugin-cedar-import-dir.test.ts | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/packages/prerender/src/graphql/__tests__/vite-plugin-cedar-import-dir.test.ts b/packages/prerender/src/graphql/__tests__/vite-plugin-cedar-import-dir.test.ts index 210a14ba17..7719fe4555 100644 --- a/packages/prerender/src/graphql/__tests__/vite-plugin-cedar-import-dir.test.ts +++ b/packages/prerender/src/graphql/__tests__/vite-plugin-cedar-import-dir.test.ts @@ -71,36 +71,38 @@ describe('vite plugin cedar import dir', () => { expect(result).toBeNull() }) - it('should handle ESM mode correctly', async () => { - const testCase = 'import-dir' - const codeFile = path.join(fixturesDir, testCase, 'code.js') - - const inputCode = fs.readFileSync(codeFile, 'utf-8') - - const vitePlugin = cedarImportDirPlugin({ projectIsEsm: true }) - const mockId = path.join(fixturesDir, testCase, 'code.js') - - const result = await callTransform(vitePlugin, inputCode, mockId) - - if ( - result && - typeof result === 'object' && - 'code' in result && - result.code - ) { - // Should include .js extensions for ESM when files are found - const hasJsExtensions = result.code.includes('.js') - const hasImports = result.code.includes('import * as') - - // If imports were generated, they should have .js extensions in ESM mode - if (hasImports) { - expect(hasJsExtensions).toBe(true) - } else { - // If no imports were generated, that's also valid (no matching files) - expect(result.code).toContain('let services = {}') - } - } - }) + // I'm not sure about this test. It seems to work better without an extension + // + // it('should handle ESM mode correctly', async () => { + // const testCase = 'import-dir' + // const codeFile = path.join(fixturesDir, testCase, 'code.js') + + // const inputCode = fs.readFileSync(codeFile, 'utf-8') + + // const vitePlugin = cedarImportDirPlugin({ projectIsEsm: true }) + // const mockId = path.join(fixturesDir, testCase, 'code.js') + + // const result = await callTransform(vitePlugin, inputCode, mockId) + + // if ( + // result && + // typeof result === 'object' && + // 'code' in result && + // result.code + // ) { + // // Should include .js extensions for ESM when files are found + // const hasJsExtensions = result.code.includes('.js') + // const hasImports = result.code.includes('import * as') + + // // If imports were generated, they should have .js extensions in ESM mode + // if (hasImports) { + // expect(hasJsExtensions).toBe(true) + // } else { + // // If no imports were generated, that's also valid (no matching files) + // expect(result.code).toContain('let services = {}') + // } + // } + // }) it('should handle missing directories gracefully', async () => { const inputCode = `import services from './test-files/**/*.{js,ts}'` From 095e9398bc578ceaa215b156cdc073f8c5b5a28a Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 25 Jul 2025 23:43:59 +0200 Subject: [PATCH 105/222] Main cedar vite plugin: Make options optional --- packages/vite/src/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/vite/src/index.ts b/packages/vite/src/index.ts index 2888bacc40..15ad476048 100644 --- a/packages/vite/src/index.ts +++ b/packages/vite/src/index.ts @@ -34,13 +34,13 @@ export { cedarSwapApolloProvider } from './plugins/vite-plugin-swap-apollo-provi export { getEnvVarDefinitions } from './lib/envVarDefinitions.js' type PluginOptions = { - mode: string | undefined + mode?: string | undefined } /** * Pre-configured vite plugin, with required config for CedarJS apps. */ -export function cedar({ mode }: PluginOptions): PluginOption[] { +export function cedar({ mode }: PluginOptions = {}): PluginOption[] { const rwConfig = getConfig() const rscEnabled = rwConfig.experimental?.rsc?.enabled From dc08ce0c06b70c8751b8ff7f0f6990d3b7c0c7a0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Fri, 25 Jul 2025 23:56:32 +0200 Subject: [PATCH 106/222] update fragment fixture --- .../api/src/services/contacts/describeContacts.test.ts | 6 +++--- .../api/src/services/users/users.test.ts | 4 ++-- .../api/contacts.describeScenario.test.ts.template | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts index 701c9408ab..ad9a697c07 100644 --- a/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/contacts/describeContacts.test.ts @@ -1,7 +1,7 @@ -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' -import { contact, contacts, createContact } from './contacts' -import type { StandardScenario } from './contacts.scenarios' +import { contact, contacts, createContact } from './contacts.js' +import type { StandardScenario } from './contacts.scenarios.js' /** * Example test for describe scenario. diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts index 0e183cf8cc..f754f23d1d 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts @@ -1,5 +1,5 @@ -import { user } from './users' -import type { StandardScenario } from './users.scenarios' +import { user } from './users.js' +import type { StandardScenario } from './users.scenarios.js' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { diff --git a/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template b/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template index 88273f5d1a..512a9ad8f7 100644 --- a/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template +++ b/tasks/test-project/templates/api/contacts.describeScenario.test.ts.template @@ -1,5 +1,5 @@ -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' import { contact, contacts, From 65292abb8ffcb81ff7518686a71d05f4c39a66ff Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 00:34:24 +0200 Subject: [PATCH 107/222] trusted documents --- docs/docs/graphql/trusted-documents.md | 2 +- packages/cli-helpers/src/lib/index.ts | 5 ++++- .../__testfixtures__/alreadySetUp/input/graphql.js | 2 +- .../__testfixtures__/alreadySetUp/output/graphql.js | 2 +- .../__testfixtures__/graphql/output/graphql.js | 2 +- .../graphql/features/trustedDocuments/graphqlTransform.ts | 4 ++-- tasks/test-project/set-up-trusted-documents.ts | 2 +- 7 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/docs/graphql/trusted-documents.md b/docs/docs/graphql/trusted-documents.md index 8b78da441b..525a2b40c9 100644 --- a/docs/docs/graphql/trusted-documents.md +++ b/docs/docs/graphql/trusted-documents.md @@ -155,7 +155,7 @@ To enable trusted documents, configure `trustedDocuments` with the store. import { createGraphQLHandler } from '@cedarjs/graphql-server' // ... -import { store } from 'src/lib/trustedDocumentsStore' +import { store } from 'src/lib/trustedDocumentsStore.js' export const handler = createGraphQLHandler({ getCurrentUser, diff --git a/packages/cli-helpers/src/lib/index.ts b/packages/cli-helpers/src/lib/index.ts index 3fd0158173..71fb23e66d 100644 --- a/packages/cli-helpers/src/lib/index.ts +++ b/packages/cli-helpers/src/lib/index.ts @@ -55,7 +55,7 @@ export const transformTSToJS = (filename: string, content: string) => { export const getPrettierOptions = async () => { try { const { default: options } = await import( - `file://${path.join(getPaths().base, 'prettier.config.js')}` + `file://${path.join(getPaths().base, 'prettier.config.cjs')}` ) if (options.tailwindConfig?.startsWith('.')) { @@ -82,7 +82,10 @@ export const prettify = async ( const parser = { '.css': 'css', '.js': 'babel', + '.cjs': 'babel', + '.mjs': 'babel', '.ts': 'babel-ts', + '.mts': 'babel-ts', '.tsx': 'babel-ts', }[path.extname(templateFilename.replace('.template', ''))] diff --git a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/input/graphql.js b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/input/graphql.js index a853c61fa7..f635badb3b 100644 --- a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/input/graphql.js +++ b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/input/graphql.js @@ -9,7 +9,7 @@ import { cookieName, getCurrentUser } from 'src/lib/auth' import { db } from 'src/lib/db' import { logger } from 'src/lib/logger' -import { store } from 'src/lib/trustedDocumentsStore' +import { store } from 'src/lib/trustedDocumentsStore.js' const authDecoder = createAuthDecoder(cookieName) diff --git a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/output/graphql.js b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/output/graphql.js index a853c61fa7..f635badb3b 100644 --- a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/output/graphql.js +++ b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/alreadySetUp/output/graphql.js @@ -9,7 +9,7 @@ import { cookieName, getCurrentUser } from 'src/lib/auth' import { db } from 'src/lib/db' import { logger } from 'src/lib/logger' -import { store } from 'src/lib/trustedDocumentsStore' +import { store } from 'src/lib/trustedDocumentsStore.js' const authDecoder = createAuthDecoder(cookieName) diff --git a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/graphql/output/graphql.js b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/graphql/output/graphql.js index a853c61fa7..f635badb3b 100644 --- a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/graphql/output/graphql.js +++ b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/__testfixtures__/graphql/output/graphql.js @@ -9,7 +9,7 @@ import { cookieName, getCurrentUser } from 'src/lib/auth' import { db } from 'src/lib/db' import { logger } from 'src/lib/logger' -import { store } from 'src/lib/trustedDocumentsStore' +import { store } from 'src/lib/trustedDocumentsStore.js' const authDecoder = createAuthDecoder(cookieName) diff --git a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/graphqlTransform.ts b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/graphqlTransform.ts index 568b0d2974..aa616275e7 100644 --- a/packages/cli/src/commands/setup/graphql/features/trustedDocuments/graphqlTransform.ts +++ b/packages/cli/src/commands/setup/graphql/features/trustedDocuments/graphqlTransform.ts @@ -7,7 +7,7 @@ export default function transform(file: FileInfo, api: API) { const allImports = root.find(j.ImportDeclaration) const hasStoreImport = allImports.some((i) => { - return i.get('source').value.value === 'src/lib/trustedDocumentsStore' + return i.get('source').value.value === 'src/lib/trustedDocumentsStore.js' }) if (!hasStoreImport) { @@ -16,7 +16,7 @@ export default function transform(file: FileInfo, api: API) { .insertAfter( j.importDeclaration( [j.importSpecifier(j.identifier('store'))], - j.literal('src/lib/trustedDocumentsStore'), + j.literal('src/lib/trustedDocumentsStore.js'), ), ) } diff --git a/tasks/test-project/set-up-trusted-documents.ts b/tasks/test-project/set-up-trusted-documents.ts index 437d0bfc97..ed1bf08778 100644 --- a/tasks/test-project/set-up-trusted-documents.ts +++ b/tasks/test-project/set-up-trusted-documents.ts @@ -53,7 +53,7 @@ async function runCommand() { 'api/src/functions/graphql.ts', ) const graphqlHandlerContent = fs.readFileSync(graphqlHandlerPath, 'utf-8') - const storeImport = "import { store } from 'src/lib/trustedDocumentsStore'" + const storeImport = "import { store } from 'src/lib/trustedDocumentsStore.js'" if (!graphqlHandlerContent.includes(storeImport)) { console.error( From dd56ef186b3646d5ba7ee302847c752e511dd70b Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 00:57:54 +0200 Subject: [PATCH 108/222] update fragments test project fixture --- .../migration.sql | 0 .../api/src/services/groceries.ts | 4 ++-- .../src/services/produces/produces.scenarios.ts | 16 ++++++++-------- .../api/src/services/produces/produces.test.ts | 16 ++++++++-------- .../api/src/services/stalls/stalls.scenarios.ts | 4 ++-- .../api/src/services/stalls/stalls.test.ts | 4 ++-- .../api/src/services/users/users.test.ts | 4 ++-- __fixtures__/fragment-test-project/package.json | 3 ++- .../fragment-test-project/vitest.config.ts | 2 +- 9 files changed, 27 insertions(+), 26 deletions(-) rename __fixtures__/fragment-test-project/api/db/migrations/{20250723113422_create_produce_stall => 20250725225512_create_produce_stall}/migration.sql (100%) diff --git a/__fixtures__/fragment-test-project/api/db/migrations/20250723113422_create_produce_stall/migration.sql b/__fixtures__/fragment-test-project/api/db/migrations/20250725225512_create_produce_stall/migration.sql similarity index 100% rename from __fixtures__/fragment-test-project/api/db/migrations/20250723113422_create_produce_stall/migration.sql rename to __fixtures__/fragment-test-project/api/db/migrations/20250725225512_create_produce_stall/migration.sql diff --git a/__fixtures__/fragment-test-project/api/src/services/groceries.ts b/__fixtures__/fragment-test-project/api/src/services/groceries.ts index 09eb5de330..834b39eea5 100644 --- a/__fixtures__/fragment-test-project/api/src/services/groceries.ts +++ b/__fixtures__/fragment-test-project/api/src/services/groceries.ts @@ -1,6 +1,6 @@ -import { Produce } from 'types/graphql' +import type { Produce } from 'types/graphql' -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' const isFruit = (grocery: Produce) => { return grocery.isSeedless !== null && grocery.ripenessIndicators !== null diff --git a/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts index 0773813183..388c48ab63 100644 --- a/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/produces/produces.scenarios.ts @@ -6,20 +6,20 @@ export const standard = defineScenario({ produce: { one: { data: { - name: "String4372567", - quantity: 5121815, - price: 1146204, + name: "String5899100", + quantity: 7675914, + price: 4275602, region: "String", - stall: { create: { name: "String", stallNumber: "String4356419" } }, + stall: { create: { name: "String", stallNumber: "String1160791" } }, }, }, two: { data: { - name: "String1795238", - quantity: 2348895, - price: 3710401, + name: "String242106", + quantity: 1821444, + price: 4034000, region: "String", - stall: { create: { name: "String", stallNumber: "String8563946" } }, + stall: { create: { name: "String", stallNumber: "String6252397" } }, }, }, }, diff --git a/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts b/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts index 3de9397ddb..c1b171145f 100644 --- a/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/produces/produces.test.ts @@ -31,17 +31,17 @@ describe("produces", () => { scenario("creates a produce", async (scenario: StandardScenario) => { const result = await createProduce({ input: { - name: "String525661", - quantity: 4885196, - price: 2377066, + name: "String1940013", + quantity: 404916, + price: 4194637, region: "String", stallId: scenario.produce.two.stallId, }, }); - expect(result.name).toEqual("String525661"); - expect(result.quantity).toEqual(4885196); - expect(result.price).toEqual(2377066); + expect(result.name).toEqual("String1940013"); + expect(result.quantity).toEqual(404916); + expect(result.price).toEqual(4194637); expect(result.region).toEqual("String"); expect(result.stallId).toEqual(scenario.produce.two.stallId); }); @@ -52,10 +52,10 @@ describe("produces", () => { })) as Produce; const result = await updateProduce({ id: original.id, - input: { name: "String87747082" }, + input: { name: "String2780002" }, }); - expect(result.name).toEqual("String87747082"); + expect(result.name).toEqual("String2780002"); }); scenario("deletes a produce", async (scenario: StandardScenario) => { diff --git a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts index 41bb725b6e..f036c146a3 100644 --- a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts +++ b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.scenarios.ts @@ -4,8 +4,8 @@ import type { ScenarioData } from "@cedarjs/testing/api"; export const standard = defineScenario({ stall: { - one: { data: { name: "String", stallNumber: "String423538" } }, - two: { data: { name: "String", stallNumber: "String5801194" } }, + one: { data: { name: "String", stallNumber: "String1022393" } }, + two: { data: { name: "String", stallNumber: "String6257009" } }, }, }); diff --git a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts index 377f81c7e3..f2dc1720d4 100644 --- a/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/stalls/stalls.test.ts @@ -30,11 +30,11 @@ describe("stalls", () => { scenario("creates a stall", async () => { const result = await createStall({ - input: { name: "String", stallNumber: "String7454580" }, + input: { name: "String", stallNumber: "String9101769" }, }); expect(result.name).toEqual("String"); - expect(result.stallNumber).toEqual("String7454580"); + expect(result.stallNumber).toEqual("String9101769"); }); scenario("updates a stall", async (scenario: StandardScenario) => { diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts index f754f23d1d..0e183cf8cc 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts @@ -1,5 +1,5 @@ -import { user } from './users.js' -import type { StandardScenario } from './users.scenarios.js' +import { user } from './users' +import type { StandardScenario } from './users.scenarios' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { diff --git a/__fixtures__/fragment-test-project/package.json b/__fixtures__/fragment-test-project/package.json index c890d69f0e..e403c327ae 100644 --- a/__fixtures__/fragment-test-project/package.json +++ b/__fixtures__/fragment-test-project/package.json @@ -10,7 +10,8 @@ "devDependencies": { "@cedarjs/core": "0.0.5", "@cedarjs/project-config": "0.0.5", - "@cedarjs/testing": "0.0.5" + "@cedarjs/testing": "0.0.5", + "vitest": "3.2.4" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", diff --git a/__fixtures__/fragment-test-project/vitest.config.ts b/__fixtures__/fragment-test-project/vitest.config.ts index 79b00e32e9..bebad23e65 100644 --- a/__fixtures__/fragment-test-project/vitest.config.ts +++ b/__fixtures__/fragment-test-project/vitest.config.ts @@ -2,6 +2,6 @@ import { defineConfig } from 'vitest/config' export default defineConfig({ test: { - projects: ['/{*,!(node_modules)/**/}/vite*'], + projects: ['/{*,!(node_modules)/**/}/vite*.config.*'], }, }) From a91e4c60d110b81b118eb985cd67083778dc2ae0 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 00:58:58 +0200 Subject: [PATCH 109/222] add more file extensions to imports --- .../api/src/services/users/users.test.ts | 2 +- docs/docs/tutorial/chapter6/comment-form.md | 4 ++-- docs/docs/tutorial/chapter6/comments-schema.md | 5 ++++- docs/docs/typescript/utility-types.md | 2 +- tasks/test-project/tasks.js | 2 +- tasks/test-project/tui-tasks.ts | 2 +- 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts index 0e183cf8cc..d6cf87e422 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts @@ -1,5 +1,5 @@ import { user } from './users' -import type { StandardScenario } from './users.scenarios' +import type { StandardScenario } from './users.scenarios.js' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { diff --git a/docs/docs/tutorial/chapter6/comment-form.md b/docs/docs/tutorial/chapter6/comment-form.md index 6468992160..552d0bdf34 100644 --- a/docs/docs/tutorial/chapter6/comment-form.md +++ b/docs/docs/tutorial/chapter6/comment-form.md @@ -1425,9 +1425,9 @@ describe('comments', () => { ```tsx title="api/src/services/comments/comments.test.ts" import { comments, createComment } from './comments' // highlight-next-line -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' -import type { StandardScenario } from './comments.scenarios' +import type { StandardScenario } from './comments.scenarios.js' describe('comments', () => { scenario('returns all comments', async (scenario) => { diff --git a/docs/docs/tutorial/chapter6/comments-schema.md b/docs/docs/tutorial/chapter6/comments-schema.md index 7acb407271..0276021e88 100644 --- a/docs/docs/tutorial/chapter6/comments-schema.md +++ b/docs/docs/tutorial/chapter6/comments-schema.md @@ -829,7 +829,10 @@ describe('comments', () => { import { comments, createComment } from './comments' // highlight-next-line -import type { StandardScenario, PostOnlyScenario } from './comments.scenarios' +import type { + StandardScenario, + PostOnlyScenario, +} from './comments.scenarios.js' describe('comments', () => { scenario('returns all comments', async (scenario: StandardScenario) => { diff --git a/docs/docs/typescript/utility-types.md b/docs/docs/typescript/utility-types.md index 951afd27f9..f2018ed1ae 100644 --- a/docs/docs/typescript/utility-types.md +++ b/docs/docs/typescript/utility-types.md @@ -152,7 +152,7 @@ export type StandardScenario = ScenarioData ``` ```ts title="api/src/services/posts/posts.test.ts" -import type { StandardScenario } from './posts.scenarios' +import type { StandardScenario } from './posts.scenarios.js' scenario('returns a single post', async (scenario: StandardScenario) => { const result = await post({ id: scenario.post.one.id }) diff --git a/tasks/test-project/tasks.js b/tasks/test-project/tasks.js index 98f77fbfd4..490a2ffc46 100644 --- a/tasks/test-project/tasks.js +++ b/tasks/test-project/tasks.js @@ -707,7 +707,7 @@ export default DoublePage` ) const test = `import { user } from './users' - import type { StandardScenario } from './users.scenarios' + import type { StandardScenario } from './users.scenarios.js' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { diff --git a/tasks/test-project/tui-tasks.ts b/tasks/test-project/tui-tasks.ts index 8171c02f71..a35d37eae5 100644 --- a/tasks/test-project/tui-tasks.ts +++ b/tasks/test-project/tui-tasks.ts @@ -807,7 +807,7 @@ export default DoublePage` ) const test = `import { user } from './users' - import type { StandardScenario } from './users.scenarios' + import type { StandardScenario } from './users.scenarios.js' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { From fff1fe2d2f1e5602a1f03485e0c3b1658cfeb8d8 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 01:05:43 +0200 Subject: [PATCH 110/222] Update test project fixture --- .../api/src/services/contacts/describeContacts.test.ts | 6 +++--- .../test-project/api/src/services/users/users.test.ts | 2 +- __fixtures__/test-project/package.json | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/__fixtures__/test-project/api/src/services/contacts/describeContacts.test.ts b/__fixtures__/test-project/api/src/services/contacts/describeContacts.test.ts index 701c9408ab..ad9a697c07 100644 --- a/__fixtures__/test-project/api/src/services/contacts/describeContacts.test.ts +++ b/__fixtures__/test-project/api/src/services/contacts/describeContacts.test.ts @@ -1,7 +1,7 @@ -import { db } from 'src/lib/db' +import { db } from 'src/lib/db.js' -import { contact, contacts, createContact } from './contacts' -import type { StandardScenario } from './contacts.scenarios' +import { contact, contacts, createContact } from './contacts.js' +import type { StandardScenario } from './contacts.scenarios.js' /** * Example test for describe scenario. diff --git a/__fixtures__/test-project/api/src/services/users/users.test.ts b/__fixtures__/test-project/api/src/services/users/users.test.ts index 0e183cf8cc..d6cf87e422 100644 --- a/__fixtures__/test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/test-project/api/src/services/users/users.test.ts @@ -1,5 +1,5 @@ import { user } from './users' -import type { StandardScenario } from './users.scenarios' +import type { StandardScenario } from './users.scenarios.js' describe('users', () => { scenario('returns a single user', async (scenario: StandardScenario) => { diff --git a/__fixtures__/test-project/package.json b/__fixtures__/test-project/package.json index 224d1e5171..b6623fdc29 100644 --- a/__fixtures__/test-project/package.json +++ b/__fixtures__/test-project/package.json @@ -11,8 +11,8 @@ "@cedarjs/core": "0.0.5", "@cedarjs/project-config": "0.0.5", "@cedarjs/testing": "0.0.5", - "prettier-plugin-tailwindcss": "^0.5.12", - "vitest": "3.2.4" + "vitest": "3.2.4", + "prettier-plugin-tailwindcss": "^0.5.12" }, "eslintConfig": { "extends": "@cedarjs/eslint-config", @@ -30,4 +30,4 @@ "react-is": "19.0.0-rc-f2df5694-20240916", "vite": "5.4.16" } -} +} \ No newline at end of file From 8f1689d0b44aa19db2bdd98da03f23f0c3889f35 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 01:26:38 +0200 Subject: [PATCH 111/222] Fix cli-helpers test --- packages/cli-helpers/package.json | 1 + .../src/lib/__tests__/index.test.ts | 20 +++++- yarn.lock | 65 +++++++++++++++++++ 3 files changed, 84 insertions(+), 2 deletions(-) diff --git a/packages/cli-helpers/package.json b/packages/cli-helpers/package.json index 974389e8d4..6797964dec 100644 --- a/packages/cli-helpers/package.json +++ b/packages/cli-helpers/package.json @@ -80,6 +80,7 @@ "@types/semver": "^7", "@types/yargs": "17.0.33", "@types/yargs-parser": "21.0.3", + "prettier-plugin-tailwindcss": "^0.6.14", "tsx": "4.20.3", "typescript": "5.6.2", "vitest": "3.2.4" diff --git a/packages/cli-helpers/src/lib/__tests__/index.test.ts b/packages/cli-helpers/src/lib/__tests__/index.test.ts index ef1aeb28ec..36ace659f5 100644 --- a/packages/cli-helpers/src/lib/__tests__/index.test.ts +++ b/packages/cli-helpers/src/lib/__tests__/index.test.ts @@ -1,6 +1,6 @@ import path from 'path' -import { vi, test, expect } from 'vitest' +import { beforeAll, afterAll, vi, test, expect } from 'vitest' import { prettify } from '../index.js' @@ -10,13 +10,26 @@ vi.mock('../paths', () => { return { base: path.resolve( __dirname, - '../../../../../__fixtures__/example-todo-main', + '../../../../../__fixtures__/test-project', ), } }, } }) +const RWJS_CWD = process.env.RWJS_CWD + +beforeAll(() => { + process.env.RWJS_CWD = path.resolve( + __dirname, + '../../../../../__fixtures__/test-project', + ) +}) + +afterAll(() => { + process.env.RWJS_CWD = RWJS_CWD +}) + test('prettify formats tsx content', async () => { const content = `import React from 'react' @@ -35,6 +48,9 @@ test('prettify formats tsx content', async () => { return <>{foo}, {bar}}` + // The test project we're pointing to during this test uses tailwind, so for + // this `expect` to pass, we need to have prettier-plugin-tailwindcss + // installed. That's why it's a dev dependency for this package expect( await prettify('FooBarComponent.template.tsx', content), ).toMatchSnapshot() diff --git a/yarn.lock b/yarn.lock index fa4b6a2076..933b9a09ec 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2673,6 +2673,7 @@ __metadata: lodash: "npm:4.17.21" pascalcase: "npm:1.0.0" prettier: "npm:3.5.3" + prettier-plugin-tailwindcss: "npm:^0.6.14" prompts: "npm:2.4.2" semver: "npm:7.6.3" smol-toml: "npm:1.3.4" @@ -25241,6 +25242,70 @@ __metadata: languageName: node linkType: hard +"prettier-plugin-tailwindcss@npm:^0.6.14": + version: 0.6.14 + resolution: "prettier-plugin-tailwindcss@npm:0.6.14" + peerDependencies: + "@ianvs/prettier-plugin-sort-imports": "*" + "@prettier/plugin-hermes": "*" + "@prettier/plugin-oxc": "*" + "@prettier/plugin-pug": "*" + "@shopify/prettier-plugin-liquid": "*" + "@trivago/prettier-plugin-sort-imports": "*" + "@zackad/prettier-plugin-twig": "*" + prettier: ^3.0 + prettier-plugin-astro: "*" + prettier-plugin-css-order: "*" + prettier-plugin-import-sort: "*" + prettier-plugin-jsdoc: "*" + prettier-plugin-marko: "*" + prettier-plugin-multiline-arrays: "*" + prettier-plugin-organize-attributes: "*" + prettier-plugin-organize-imports: "*" + prettier-plugin-sort-imports: "*" + prettier-plugin-style-order: "*" + prettier-plugin-svelte: "*" + peerDependenciesMeta: + "@ianvs/prettier-plugin-sort-imports": + optional: true + "@prettier/plugin-hermes": + optional: true + "@prettier/plugin-oxc": + optional: true + "@prettier/plugin-pug": + optional: true + "@shopify/prettier-plugin-liquid": + optional: true + "@trivago/prettier-plugin-sort-imports": + optional: true + "@zackad/prettier-plugin-twig": + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-import-sort: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-style-order: + optional: true + prettier-plugin-svelte: + optional: true + checksum: 10c0/1bf635be28b30b3f171a184497eecf512601d19328e402dd2eb1ede52aa57b4f5b605eae2929f4916de9ba22526f3d730d9afa90334db09d17c59f463f7b26d8 + languageName: node + linkType: hard + "prettier@npm:3.5.3": version: 3.5.3 resolution: "prettier@npm:3.5.3" From 8938d46d1f354cd1d214301cf36fa681f251bff5 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 01:38:22 +0200 Subject: [PATCH 112/222] fix gql import path --- packages/prerender/src/build-and-import/buildAndImport.ts | 2 +- packages/web/src/global.web-auto-imports.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/prerender/src/build-and-import/buildAndImport.ts b/packages/prerender/src/build-and-import/buildAndImport.ts index 8542e1a0c9..3a4908f8ef 100644 --- a/packages/prerender/src/build-and-import/buildAndImport.ts +++ b/packages/prerender/src/build-and-import/buildAndImport.ts @@ -132,7 +132,7 @@ export async function buildAndImport( // import { gql } from 'src/graphql/gql' useTrustedDocumentsGqlTag && { name: 'gql', - from: `web/src/graphql/gql`, + from: `src/graphql/gql.js`, }, ].filter((v?: T | false): v is T => Boolean(v)), }), diff --git a/packages/web/src/global.web-auto-imports.ts b/packages/web/src/global.web-auto-imports.ts index 90895d6950..3eb0d251b5 100644 --- a/packages/web/src/global.web-auto-imports.ts +++ b/packages/web/src/global.web-auto-imports.ts @@ -9,7 +9,7 @@ declare global { // This type is used for both regular RW projects and projects that have // enabled Trusted Documents. For regular RW projects, this could have been // typed just by importing gql from `graphql-tag`. But for Trusted Documents - // the type should be imported from `web/src/graphql/gql` in the user's + // the type should be imported from `web/src/graphql/gql.js` in the user's // project. The type here is generic enough to cover both cases. const gql: ( source: string | TemplateStringsArray | readonly string[], From e324dab21b95944278346d901722164388482cd3 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 01:41:17 +0200 Subject: [PATCH 113/222] more file extensions --- .../fragment-test-project/api/src/services/users/users.test.ts | 2 +- __fixtures__/test-project/api/src/services/users/users.test.ts | 2 +- tasks/test-project/tasks.js | 2 +- tasks/test-project/tui-tasks.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts index d6cf87e422..f754f23d1d 100644 --- a/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/fragment-test-project/api/src/services/users/users.test.ts @@ -1,4 +1,4 @@ -import { user } from './users' +import { user } from './users.js' import type { StandardScenario } from './users.scenarios.js' describe('users', () => { diff --git a/__fixtures__/test-project/api/src/services/users/users.test.ts b/__fixtures__/test-project/api/src/services/users/users.test.ts index d6cf87e422..f754f23d1d 100644 --- a/__fixtures__/test-project/api/src/services/users/users.test.ts +++ b/__fixtures__/test-project/api/src/services/users/users.test.ts @@ -1,4 +1,4 @@ -import { user } from './users' +import { user } from './users.js' import type { StandardScenario } from './users.scenarios.js' describe('users', () => { diff --git a/tasks/test-project/tasks.js b/tasks/test-project/tasks.js index 490a2ffc46..2703efb488 100644 --- a/tasks/test-project/tasks.js +++ b/tasks/test-project/tasks.js @@ -706,7 +706,7 @@ export default DoublePage` fullPath('api/src/services/users/users.scenarios'), ) - const test = `import { user } from './users' + const test = `import { user } from './users.js' import type { StandardScenario } from './users.scenarios.js' describe('users', () => { diff --git a/tasks/test-project/tui-tasks.ts b/tasks/test-project/tui-tasks.ts index a35d37eae5..cf2c1120c4 100644 --- a/tasks/test-project/tui-tasks.ts +++ b/tasks/test-project/tui-tasks.ts @@ -806,7 +806,7 @@ export default DoublePage` fullPath('api/src/services/users/users.scenarios'), ) - const test = `import { user } from './users' + const test = `import { user } from './users.js' import type { StandardScenario } from './users.scenarios.js' describe('users', () => { From 420a382b44278d142d046557ecf55f4413287750 Mon Sep 17 00:00:00 2001 From: Tobbe Lundberg Date: Sat, 26 Jul 2025 08:12:44 +0200 Subject: [PATCH 114/222] Remove unused code --- .../vite/src/build/__tests__/buildWeb.test.ts | 139 ----- .../fixtures/nestedPages/redwood.toml | 16 - .../fixtures/nestedPages/web/src/Routes.js | 62 -- .../fixtures/nestedPages/web/src/auth.ts | 3 - .../fixtures/nestedPages/web/src/index.html | 21 - .../src/layouts/AdminLayout/AdminLayout.tsx | 56 -- .../web/src/layouts/MainLayout/MainLayout.tsx | 340 ----------- .../layouts/ShowcaseLayout/ShowcaseLayout.tsx | 27 - .../Admin/User/EditUserPage/EditUserPage.tsx | 11 - .../Admin/User/NewUserPage/NewUserPage.tsx | 7 - .../pages/Admin/User/UserPage/UserPage.tsx | 11 - .../pages/Admin/User/UsersPage/UsersPage.tsx | 7 - .../pages/FatalErrorPage/FatalErrorPage.tsx | 57 -- .../ForgotPasswordPage/ForgotPasswordPage.js | 90 --- .../web/src/pages/HomePage/HomePage.js | 543 ------------------ .../src/pages/HomePage/HomePage.stories.js | 7 - .../web/src/pages/HomePage/HomePage.test.js | 14 - .../AllJobProfilesPage/AllJobProfilesPage.tsx | 33 -- .../pages/Jobs/AllJobsPage/AllJobsPage.tsx | 31 - .../pages/Jobs/EditJobPage/EditJobPage.tsx | 9 - .../EditJobProfilePage/EditJobProfilePage.tsx | 7 - .../web/src/pages/Jobs/JobPage/JobPage.tsx | 17 - .../Jobs/JobProfilePage/JobProfilePage.tsx | 17 - .../web/src/pages/Jobs/JobsPage/JobsPage.tsx | 55 -- .../src/pages/Jobs/NewJobPage/NewJobPage.tsx | 34 -- .../NewJobProfilePage/NewJobProfilePage.tsx | 66 --- .../web/src/pages/LoginPage/LoginPage.js | 128 ----- .../src/pages/NotFoundPage/NotFoundPage.js | 45 -- .../src/build/__tests__/nestedPages.test.ts | 254 -------- packages/vite/src/build/build.ts | 67 --- 30 files changed, 2174 deletions(-) delete mode 100644 packages/vite/src/build/__tests__/buildWeb.test.ts delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/redwood.toml delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/Routes.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/auth.ts delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/index.html delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/AdminLayout/AdminLayout.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/MainLayout/MainLayout.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/ShowcaseLayout/ShowcaseLayout.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/EditUserPage/EditUserPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/NewUserPage/NewUserPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UserPage/UserPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UsersPage/UsersPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/FatalErrorPage/FatalErrorPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/ForgotPasswordPage/ForgotPasswordPage.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/HomePage/HomePage.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/HomePage/HomePage.stories.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/HomePage/HomePage.test.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/AllJobProfilesPage/AllJobProfilesPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/AllJobsPage/AllJobsPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/EditJobPage/EditJobPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/EditJobProfilePage/EditJobProfilePage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/JobPage/JobPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/JobProfilePage/JobProfilePage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/JobsPage/JobsPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/NewJobPage/NewJobPage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Jobs/NewJobProfilePage/NewJobProfilePage.tsx delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/LoginPage/LoginPage.js delete mode 100644 packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/NotFoundPage/NotFoundPage.js delete mode 100644 packages/vite/src/build/__tests__/nestedPages.test.ts diff --git a/packages/vite/src/build/__tests__/buildWeb.test.ts b/packages/vite/src/build/__tests__/buildWeb.test.ts deleted file mode 100644 index 9e1cb1c242..0000000000 --- a/packages/vite/src/build/__tests__/buildWeb.test.ts +++ /dev/null @@ -1,139 +0,0 @@ -import path from 'node:path' -import { performance } from 'node:perf_hooks' - -import { beforeEach, test, expect, afterAll } from 'vitest' - -import { findWebFiles } from '@cedarjs/internal/dist/files.js' -import { ensurePosixPath, getPaths } from '@cedarjs/project-config' - -import { prebuildWebFiles, prebuildWebFile, cleanWebBuild } from '../build.js' - -const FIXTURE_PATH = path.resolve( - __dirname, - '../../../../../__fixtures__/example-todo-main', -) - -function cleanPaths(p: string) { - return ensurePosixPath(path.relative(FIXTURE_PATH, p)) -} - -beforeEach(() => { - process.env.RWJS_CWD = FIXTURE_PATH - cleanWebBuild() -}) - -afterAll(() => { - delete process.env.RWJS_CWD -}) - -test('web files are prebuilt (no prerender)', { timeout: 10_000 }, async () => { - let perfNow = performance.now() - const webFiles = findWebFiles() - - // This is ~5ms on my local machine. - // It failed once on Ubutu CI taking ~65ms when I had the limit be 50ms - expect(performance.now() - perfNow).toBeLessThan(100) - - perfNow = performance.now() - const prebuiltFiles = await prebuildWebFiles(webFiles, { - forJest: true, - }) - - // This is ~500ms on my local machine. - // ~1200ms on Ubuntu CI, ~1900ms on Windows CI - // Occationally this is > 6s on CI - expect( - performance.now() - perfNow, - 'prebuildWebFiles execution time', - ).toBeLessThan(7500) - - const relativePaths = prebuiltFiles - .filter((x) => typeof x !== 'undefined') - .map(cleanPaths) - .sort() - - // Builds non-nested functions - expect(relativePaths).toMatchInlineSnapshot(` - [ - ".redwood/prebuild/web/src/App.js", - ".redwood/prebuild/web/src/Routes.js", - ".redwood/prebuild/web/src/components/AddTodo/AddTodo.js", - ".redwood/prebuild/web/src/components/AddTodoControl/AddTodoControl.js", - ".redwood/prebuild/web/src/components/Check/Check.js", - ".redwood/prebuild/web/src/components/NumTodosCell/NumTodosCell.js", - ".redwood/prebuild/web/src/components/NumTodosTwoCell/NumTodosTwoCell.js", - ".redwood/prebuild/web/src/components/TableCell/TableCell.js", - ".redwood/prebuild/web/src/components/TodoItem/TodoItem.js", - ".redwood/prebuild/web/src/components/TodoListCell/TodoListCell.tsx", - ".redwood/prebuild/web/src/graphql/fragment-masking.js", - ".redwood/prebuild/web/src/graphql/gql.js", - ".redwood/prebuild/web/src/graphql/graphql.js", - ".redwood/prebuild/web/src/graphql/index.js", - ".redwood/prebuild/web/src/layouts/SetLayout/SetLayout.js", - ".redwood/prebuild/web/src/pages/BarPage/BarPage.tsx", - ".redwood/prebuild/web/src/pages/FatalErrorPage/FatalErrorPage.js", - ".redwood/prebuild/web/src/pages/FooPage/FooPage.tsx", - ".redwood/prebuild/web/src/pages/HomePage/HomePage.tsx", - ".redwood/prebuild/web/src/pages/NotFoundPage/NotFoundPage.js", - ".redwood/prebuild/web/src/pages/PrivatePage/PrivatePage.tsx", - ".redwood/prebuild/web/src/pages/TypeScriptPage/TypeScriptPage.tsx", - ".redwood/prebuild/web/src/pages/admin/EditUserPage/EditUserPage.jsx", - ] - `) -}) - -test('Check routes are imported with require when staticImports flag is enabled', async () => { - const routesFile = getPaths().web.routes - - const built = await prebuildWebFile(routesFile, { - forPrerender: true, - forJest: true, - }) - const prerendered = built?.code - - /* Check that imports have the form - `const HomePage = { - name: "HomePage", - loader: () => require("` 👈 Uses a require statement - */ - expect(prerendered).toContain(`const HomePage = {`) - expect(prerendered).toContain(`const BarPage = {`) - - /* - 👇 Foo page is an explicitly imported page in the source - const FooPage = { - name: "FooPage", - loader: () => require( - */ - expect(prerendered).toContain(`const FooPage = {`) - expect(prerendered).not.toContain( - `var _FooPage = _interopRequireDefault(require(`, - ) -}) - -test('Check routes are imported with "import" when staticImports flag is NOT passed', async () => { - const routesFile = getPaths().web.routes - - const built = await prebuildWebFile(routesFile, { - forJest: true, - }) - const withoutStaticImports = built?.code - - /* Check that imports have the form - `const HomePage = { - name: "HomePage", - loader: () => import("` 👈 Uses an (async) import statement - */ - - expect(withoutStaticImports).toContain(`const HomePage = {`) - expect(withoutStaticImports).toContain(`const BarPage = {`) - - /* - 👇 Foo page is an explicitly imported page, so it should - import FooPage from "..."; - */ - expect(withoutStaticImports).not.toContain(`const FooPage = {`) - expect(withoutStaticImports).toContain( - `var _FooPage = _interopRequireDefault(require(`, - ) -}) diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/redwood.toml b/packages/vite/src/build/__tests__/fixtures/nestedPages/redwood.toml deleted file mode 100644 index 2b820da9bd..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/redwood.toml +++ /dev/null @@ -1,16 +0,0 @@ -# This file contains the configuration settings for your Redwood app. -# This file is also what makes your Redwood app a Redwood app. -# If you remove it and try to run `yarn rw dev`, you'll get an error. -# -# For the full list of options, see the "App Configuration: redwood.toml" doc: -# https://redwoodjs.com/docs/app-configuration-redwood-toml - -[web] - title = "RedwoodJS.com" - port = 8910 - apiUrl = "/.netlify/functions" - includeEnvironmentVariables = [] # any ENV vars that should be available to the web side, see https://redwoodjs.com/docs/environment-variables#web -[api] - port = 8911 -[browser] - open = true diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/Routes.js b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/Routes.js deleted file mode 100644 index 3149587548..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/Routes.js +++ /dev/null @@ -1,62 +0,0 @@ -// In this file, all Page components from 'src/pages` are auto-imported. Nested -// directories are supported, and should be uppercase. Each subdirectory will be -// prepended onto the component name. -// -// Examples: -// -// 'src/pages/HomePage/HomePage.js' -> HomePage -// 'src/pages/Admin/BooksPage/BooksPage.js' -> AdminBooksPage - -// Private is deprecated, but we still want to test it so we don't break -// people's projects that still use it. -import { Private, Route, Router, Set } from '@cedarjs/router' - -import AdminLayout from 'src/layouts/AdminLayout/AdminLayout' -import MainLayout from 'src/layouts/MainLayout/MainLayout' - -import JobsPage from 'src/pages/Jobs/JobsPage' -import JobPage from 'src/pages/Jobs/JobPage' -import NewJobPage from 'src/pages/Jobs/NewJobPage' -import EditJobPage, { NonDefaultExport } from 'src/pages/Jobs/EditJobPage' -import BazingaJobProfilePageWithFunnyName from 'src/pages/Jobs/JobProfilePage' -import NewJobProfilePage from 'src/pages/Jobs/NewJobProfilePage' -import EditJobProfilePage from 'src/pages/Jobs/EditJobProfilePage' -import AllJobsPage from 'src/pages/Jobs/AllJobsPage' -import AllJobProfilesPage from 'src/pages/Jobs/AllJobProfilesPage' - -const Routes = () => { - console.log(NonDefaultExport) - return ( - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ) -} - -export default Routes diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/auth.ts b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/auth.ts deleted file mode 100644 index 38f63b5202..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/auth.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { createAuthentication } from "@cedarjs/auth"; - -export const { AuthProvider, useAuth } = createAuthentication({} as any) diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/index.html b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/index.html deleted file mode 100644 index 902339287e..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - -
- - <%= prerenderPlaceholder %> -
- - - diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/AdminLayout/AdminLayout.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/AdminLayout/AdminLayout.tsx deleted file mode 100644 index 96b0dd9d82..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/AdminLayout/AdminLayout.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import { Link, routes } from '@cedarjs/router' -import { Toaster } from '@cedarjs/web/toast' - -import { useAuth } from '../../auth' - -type AdminLayoutProps = { - children?: React.ReactNode -} - -const AdminLayout = ({children}: AdminLayoutProps) => { - const {logOut, hasRole} = useAuth() - - return ( -
- -
    - {hasRole(["editor", "admin"]) && (<> -
  • - Showcases -
  • -
  • - Authors -
  • -
  • - Tags -
  • -
  • - Medias -
  • - )} - {hasRole("admin") && ( -
  • - Users -
  • - )} - {hasRole(["editor", "admin", "translator"]) && -
  • - Localizations -
  • - } -
  • - { - logOut() - }} - > - -> Log out - -
  • -
-
{children}
-
- ) -} - -export default AdminLayout diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/MainLayout/MainLayout.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/MainLayout/MainLayout.tsx deleted file mode 100644 index 4876e84090..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/MainLayout/MainLayout.tsx +++ /dev/null @@ -1,340 +0,0 @@ -import { Link, routes } from '@cedarjs/router' -import { Toaster } from '@cedarjs/web/toast' -import { Popover, Transition } from '@headlessui/react' -import { Fragment } from 'react' -import i18n, { Languages } from 'src/i18n' - -type MainLayoutProps = { - children?: React.ReactNode -} - -const PopoverLink = ({ children, route, link, title, desc }) => { - const body = ( - <> -
- {children} -
-
-

{title}

-

{desc}

-
- - ) - const linkClassName = - 'flex items-center p-2 -m-3 transition duration-150 ease-in-out rounded-lg hover:bg-stone-50 focus:outline-none focus-visible:ring focus-visible:ring-orange-500 focus-visible:ring-opacity-50 no-underline' - - if (route) { - return ( - - {body} - - ) - } else { - return ( - - {body} - - ) - } -} - -const MainLayout = ({ children }: MainLayoutProps) => { - return ( - <> - -
-
-
-
- - RedwoodJS Logo -

- RedwoodJS Logo -

- - -
- -
-
-
- - - - -
-
-
    - {/*
  • - - Start Tutorial - -
  • */} -
  • - -
    - star11,432 -
    -
    -
    -
    -
    -
  • -
  • - - - - - -
  • -
  • - - {(open) => ( - <> - - - language - - - - -
    -
    - {Object.values(Languages).map((ln) => ( - - ))} -
    -
    -
    -
    - - )} -
    -
  • -
-
-
-
-
- -
{children}
- -
- Copyright ©{new Date().getFullYear()} Tom Preston-Werner -
- - ) -} - -export default MainLayout diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/ShowcaseLayout/ShowcaseLayout.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/ShowcaseLayout/ShowcaseLayout.tsx deleted file mode 100644 index 199a4c1223..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/layouts/ShowcaseLayout/ShowcaseLayout.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { FC } from 'react' -import { Link, routes } from '@cedarjs/router' - -const ShowcaseLayout: FC = ({ children }) => { - return ( - <> -
- -
- {children} - - ) -} - -export default ShowcaseLayout diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/EditUserPage/EditUserPage.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/EditUserPage/EditUserPage.tsx deleted file mode 100644 index 8e2a35703f..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/EditUserPage/EditUserPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import EditUserCell from 'src/components/User/EditUserCell' - -type UserPageProps = { - id: number -} - -const EditUserPage = ({ id }: UserPageProps) => { - return -} - -export default EditUserPage diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/NewUserPage/NewUserPage.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/NewUserPage/NewUserPage.tsx deleted file mode 100644 index c9b6dceb8a..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/NewUserPage/NewUserPage.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import NewUser from 'src/components/User/NewUser' - -const NewUserPage = () => { - return -} - -export default NewUserPage diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UserPage/UserPage.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UserPage/UserPage.tsx deleted file mode 100644 index 3f298a4e32..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UserPage/UserPage.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import UserCell from 'src/components/User/UserCell' - -type UserPageProps = { - id: number -} - -const UserPage = ({ id }: UserPageProps) => { - return -} - -export default UserPage diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UsersPage/UsersPage.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UsersPage/UsersPage.tsx deleted file mode 100644 index fb7b175c9d..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/Admin/User/UsersPage/UsersPage.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import UsersCell from 'src/components/User/UsersCell' - -const UsersPage = () => { - return -} - -export default UsersPage diff --git a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/FatalErrorPage/FatalErrorPage.tsx b/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/FatalErrorPage/FatalErrorPage.tsx deleted file mode 100644 index 8afcee8498..0000000000 --- a/packages/vite/src/build/__tests__/fixtures/nestedPages/web/src/pages/FatalErrorPage/FatalErrorPage.tsx +++ /dev/null @@ -1,57 +0,0 @@ -// This page will be rendered when an error makes it all the way to the top of the -// application without being handled by a Javascript catch statement or React error -// boundary. -// -// 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 :) - -// This import will be automatically removed when building for production -import { DevFatalErrorPage } from '@cedarjs/web/dist/components/DevFatalErrorPage' - -export default DevFatalErrorPage || - (() => ( -
-