Skip to content

Commit

Permalink
Replicate Decls in TS wasp-config package
Browse files Browse the repository at this point in the history
  • Loading branch information
sodic committed Sep 30, 2024
1 parent 934e8ae commit 384116a
Show file tree
Hide file tree
Showing 4 changed files with 223 additions and 6 deletions.
199 changes: 199 additions & 0 deletions waspc/packages/wasp-config/src/decls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
// TODO: This whole file is not DRY, we have the AppSpec specified in two
// diffeernent places: Haskell code and hare.

export type Decl =
| { declType: 'page'; declName: string; declValue: Page }
| { declType: 'route'; declName: string; declValue: Route }
| { declType: 'query'; declName: string; declValue: Query }
| { declType: 'action'; declName: string; declValue: Action }
| { declType: 'app'; declName: string; declValue: App }
| { declType: 'entity'; declName: string; declValue: Entity }
| { declType: 'entity'; declName: string; declValue: Entity }
| { declType: 'job'; declName: string; declValue: Job }
| { declType: 'api'; declName: string; declValue: Api }
| { declType: 'apiNamespace'; declName: string; declValue: ApiNamespace }
| { declType: 'crud'; declName: string; declValue: Crud }

type Page = {
component: ExtImport
authRequired?: boolean
}

type Route = {
path: string
to: Ref<'page'>
}

type Action = {
fn: ExtImport
entities?: Ref<'entity'>[]
auth?: boolean
}

type Query = {
fn: ExtImport
entities?: Ref<'entity'>[]
auth?: boolean
}

type Job = {
executor: JobExecutor
perofrm: {
fn: ExtImport
executorOptions?: ExecutorOptions
}
schedule?: {
cron: string
args?: object
executorOption?: ExecutorOptions
}
entities?: Ref<'entity'>[]
}

type Api = {
fn: ExtImport
middlewareConfigFn?: ExtImport
entities?: Ref<'entity'>[]
httpRoute: [[HttpMethod, string]]
auth?: boolean
}

type ApiNamespace = {
middlewareConfigFn: ExtImport
path: String
}

type Crud = {
entity: Ref<'entity'>
operations: CrudOperations
}

type Entity = {}

type App = {
wasp: Wasp
title: string
head?: string[]
auth?: Auth
server?: Server
client?: Client
db?: Db
emailSender?: EmailSender
webSocket?: WebSocket
}

type ExtImport = {
kind: 'named' | 'default'
name: string
path: `@src/${string}`
}

type JobExecutor = 'PgBoss'

type ExecutorOptions = {
pgBoss?: object
}

type HttpMethod = 'ALL' | 'GET' | 'POST' | 'PUT' | 'DELETE'

type CrudOperations = {
get?: CrudOperationOptions
getAll?: CrudOperationOptions
create?: CrudOperationOptions
update?: CrudOperationOptions
delete?: CrudOperationOptions
}

type CrudOperationOptions = {
isPublic?: boolean
overrideFn?: ExtImport
}

type Wasp = {
version: string
}

type Auth = {
userEntity: Ref<'entity'>
externalAuthEntity?: Ref<'entity'>
methods: AuthMethods
onAuthFailedRedirectTo: string
onAuthSucceededRedirectTo?: string
onBeforeSignup?: ExtImport
onAfterSignup?: ExtImport
onBeforeOAuthRedirect?: ExtImport
onBeforeLogin?: ExtImport
onAfterLogin?: ExtImport
}

type AuthMethods = {
usernameAndPassword?: UsernameAndPasswordConfig
discord?: ExternalAuthConfig
google?: ExternalAuthConfig
gitHub?: ExternalAuthConfig
keycloak?: ExternalAuthConfig
email?: EmailAuthConfig
}

type UsernameAndPasswordConfig = {
userSignupFields?: ExtImport
}

type ExternalAuthConfig = {
configFn?: ExtImport
userSignupFields?: ExtImport
}

type EmailAuthConfig = {
userSignupFields?: ExtImport
fromField: EmailFromField
emailVerification: EmailVerificationConfig
passwordReset: PasswordResetConfig
}

type EmailSender = {
provider: EmailProvider
defaultFrom?: EmailFromField
}

// TODO: duplication
type EmailProvider = 'SMTP' | 'SendGrid' | 'Mailgun' | 'Dummy'

type EmailFromField = {
name?: string
email: string
}

type EmailVerificationConfig = {
getEmailContentFn?: ExtImport
clientRoute: Ref<'route'>
}

type PasswordResetConfig = {
getEmailContentFn?: ExtImport
clientRoute: Ref<'route'>
}

type Ref<T extends Decl['declType']> = T & {
kind: 'validated'
}

type Server = {
setupFn?: ExtImport
middlewareConfigFn?: ExtImport
}

type Client = {
setupFn?: ExtImport
rootComponent?: ExtImport
baseDir?: `/${string}`
}

type Db = {
seeds?: ExtImport[]
}

type WebSocket = {
fn: ExtImport
autoConnect?: boolean
}
11 changes: 8 additions & 3 deletions waspc/packages/wasp-config/src/run.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/usr/bin/env node
import { writeFileSync } from 'fs'
import { App, Spec } from './lib.js'
import { Decl } from './decls.js'

async function main() {
const {
Expand All @@ -16,8 +17,12 @@ async function main() {
console.log(`Wasp spec written to ${outputFile}`)
}

function analyzeApp(app: App, entityNames: string[]): Spec {
return app.getSpec()
function analyzeApp(app: App, entityNames: string[]): Decl[] {
const userSpec = app.getSpec()
// TODO: Semantic analysis and type checking (check entities, check routes, check pages, ...)
// Check how much you need to do and how much you can offload to the wasp compiler.
// I only need to reimplement the stuff that comes before appspec validation.
return []
}

function parseProcessArguments(args: string[]): {
Expand Down Expand Up @@ -59,7 +64,7 @@ function parseEntityNamesJson(entitiesJson: string): string[] {
return entities
}

function serialize(appConfig: Spec): string {
function serialize(appConfig: Decl[]): string {
return JSON.stringify(appConfig)
}

Expand Down
3 changes: 0 additions & 3 deletions waspc/src/Wasp/AppSpec/App/Wasp.hs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ module Wasp.AppSpec.App.Wasp (Wasp (..)) where
import Data.Aeson (FromJSON)
import Data.Data (Data)
import GHC.Generics (Generic)
import Wasp.AppSpec.Core.IsDecl (IsDecl)

data Wasp = Wasp
{ version :: String
}
deriving (Show, Eq, Data, Generic, FromJSON)

instance IsDecl Wasp
16 changes: 16 additions & 0 deletions waspc/test/AppSpec/FromJSONTest.hs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import Wasp.AppSpec.Page (Page)
import qualified Wasp.AppSpec.Page as Page
import Wasp.AppSpec.Query (Query)
import qualified Wasp.AppSpec.Query as Query
import qualified Wasp.AppSpec.Route as Route

spec_AppSpecFromJSON :: Spec
spec_AppSpecFromJSON = do
Expand Down Expand Up @@ -81,6 +82,21 @@ spec_AppSpecFromJSON = do
}
|]
`shouldDecodeTo` (Nothing :: Maybe Page.Page)
describe "Route" $ do
it "parses a valid Route JSON" $ do
[trimming|
{
"path": "/foo",
"to": ${pageRef}
}
|]
`shouldDecodeTo` Just
( Route.Route
{ Route.path = "/foo",
Route.to = fromJust $ decodeJson pageRef
}
)

describe "Ref" $ do
it "parses a valid Entity Ref JSON" $ do
fooEntityRef `shouldDecodeTo` Just (Ref.Ref "foo" :: Ref Entity)
Expand Down

0 comments on commit 384116a

Please sign in to comment.