Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Filip ts config cleanup all #2435

Merged
merged 26 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
02a6639
Clean up Wasp ts config files
sodic Nov 28, 2024
f4e3188
Add more refactors to the TS SDK
sodic Nov 29, 2024
01038ec
Add even more refactors to the TS SDK
sodic Dec 2, 2024
3584c24
Turn Decl in appSpec.ts into a generic
sodic Dec 2, 2024
e8badd2
Replace case with fromMaybe
sodic Dec 9, 2024
6d03463
Move Wasp file logic into a separate module
sodic Dec 10, 2024
9638e32
Add elaborating comments and improve type for wasp file
sodic Dec 10, 2024
a1bd9ab
Change case to either
sodic Dec 10, 2024
a79e19b
Move Job helpers out of the generator
sodic Dec 10, 2024
f84b832
Add elaborating comment to analyze
sodic Dec 10, 2024
c7932a9
Inline getModelNames
sodic Dec 10, 2024
ae52cd2
Address PR comments
sodic Dec 20, 2024
1528874
Merge branch 'filip-ts-config-cleanup' into filip-ts-config-cleanup-2
sodic Dec 20, 2024
026b48c
Address PR comments
sodic Dec 20, 2024
43e1801
Merge branch 'filip-ts-config-cleanup-2' into filip-ts-config-cleanup-3
sodic Dec 20, 2024
4ab6e94
Separate WaspFile into modules
sodic Dec 20, 2024
d2cad42
Address PR comments
sodic Dec 20, 2024
0d8da66
Fix wasp file
sodic Dec 20, 2024
63c56d4
Fix formatting
sodic Dec 20, 2024
f15068f
Merge branch 'filip-ts-config-cleanup-all' into filip-ts-config-cleanup
sodic Jan 2, 2025
d97c1b0
Merge branch 'filip-ts-config-cleanup' into filip-ts-config-cleanup-2
sodic Jan 2, 2025
cc187a9
Fix typo
sodic Jan 2, 2025
559656a
Merge branch 'filip-ts-config-cleanup' into filip-ts-config-cleanup-2
sodic Jan 2, 2025
6b59874
Merge branch 'filip-ts-config-cleanup-2' into filip-ts-config-cleanup-3
sodic Jan 2, 2025
9a9e3d8
Remove redundant part of the implementation comment
sodic Jan 2, 2025
fc394b2
Merge branch 'filip-ts-config-cleanup-2' into filip-ts-config-cleanup-3
sodic Jan 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ import StrongPath (Abs, Dir, File, Path')
import Wasp.Cli.Command.CreateNewProject.Common (defaultWaspVersionBounds)
import Wasp.Cli.Command.CreateNewProject.ProjectDescription (NewProjectAppName, NewProjectName)
import Wasp.NodePackageFFI (InstallablePackage (WaspConfigPackage), getPackageInstallationPath)
import Wasp.Project.Analyze (WaspFilePath (..), findWaspFile)
import Wasp.Project.Analyze (WaspFilePath (..))
import Wasp.Project.Common (WaspProjectDir)
import Wasp.Project.ExternalConfig.PackageJson (findPackageJsonFile)
import Wasp.Project.WaspFile (findWaspFile)
import qualified Wasp.Util.IO as IOUtil

replaceTemplatePlaceholdersInTemplateFiles :: NewProjectAppName -> NewProjectName -> Path' Abs (Dir WaspProjectDir) -> IO ()
Expand Down Expand Up @@ -52,7 +53,6 @@ replaceTemplatePlaceholdersInFileOnDisk appName projectName file = do
("__waspProjectName__", show projectName),
("__waspVersion__", defaultWaspVersionBounds)
]
-- TODO: We do this in all files, but not all files have all placeholders
updateFileContentWith (replacePlaceholders waspTemplateReplacements) file
where
updateFileContentWith :: (Text -> Text) -> Path' Abs (File f) -> IO ()
Expand Down
2 changes: 1 addition & 1 deletion waspc/cli/src/Wasp/Cli/Command/Db/Studio.hs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import Wasp.Cli.Command (Command)
import Wasp.Cli.Command.Message (cliSendMessageC)
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject), require)
import Wasp.Generator.DbGenerator.Jobs (runStudio)
import Wasp.Generator.Job.IO (readJobMessagesAndPrintThemPrefixed)
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
import qualified Wasp.Message as Msg
import Wasp.Project.Common (dotWaspDirInWaspProjectDir, generatedCodeDirInDotWaspDir)

Expand Down
6 changes: 3 additions & 3 deletions waspc/cli/src/Wasp/Cli/Command/TsConfigSetup.hs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import StrongPath (Abs, Dir, Path')
import System.Exit (ExitCode (..))
import Wasp.Cli.Command (Command, CommandError (..), require)
import Wasp.Cli.Command.Require (InWaspProject (InWaspProject))
import qualified Wasp.Generator.Job as J
import Wasp.Generator.Job.IO (readJobMessagesAndPrintThemPrefixed)
import Wasp.Generator.Job.Process (runNodeCommandAsJob)
import qualified Wasp.Job as J
import Wasp.Job.IO (readJobMessagesAndPrintThemPrefixed)
import Wasp.Job.Process (runNodeCommandAsJob)
import Wasp.NodePackageFFI (InstallablePackage (WaspConfigPackage), getPackageInstallationPath)

-- | Prepares the project for using Wasp's TypeScript SDK.
Expand Down
20 changes: 10 additions & 10 deletions waspc/packages/wasp-config/eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import globals from 'globals'
import pluginJs from '@eslint/js'
import tseslint from 'typescript-eslint'

export default [
pluginJs.configs.recommended,
...tseslint.configs.strict,
// Todo: explore typed-linting: https://typescript-eslint.io/getting-started/typed-linting
{
languageOptions: {
globals: globals.node,
},
},
// global ignore
{
ignores: ["node_modules/", "dist/"],
ignores: ['node_modules/', 'dist/'],
},
{
rules: {
"@typescript-eslint/no-unused-vars": "warn",
"@typescript-eslint/no-empty-function": "warn",
"no-empty": "warn",
"no-constant-condition": "warn",
'@typescript-eslint/no-unused-vars': 'warn',
'@typescript-eslint/no-empty-function': 'warn',
'no-empty': 'warn',
'no-constant-condition': 'warn',
'object-shorthand': 'warn',
},
},
];
]
189 changes: 118 additions & 71 deletions waspc/packages/wasp-config/src/appSpec.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,41 @@
/** This module is a mirror implementation of AppSpec Decls in TypeScript.
* The original implemention is in Haskell (waspc).
/** This module is a mirror implementation of FromJSON for AppSpec Decls in
* TypeScript. The original implemention is in Haskell (waspc).
*
* IMPORTANT: Do not change this file without updating the AppSpec in waspc.
*/

export type Decl =
| { declType: 'App'; declName: string; declValue: App }
| { 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: 'Job'; declName: string; declValue: Job }
| { declType: 'Api'; declName: string; declValue: Api }
| { declType: 'ApiNamespace'; declName: string; declValue: ApiNamespace }
| { declType: 'Crud'; declName: string; declValue: Crud }
export type Decl = {
[Type in keyof DeclTypeToValue]: {
declType: Type
declName: string
declValue: DeclTypeToValue[Type]
}
}[keyof DeclTypeToValue]

export type DeclTypeToValue = {
App: App
Page: Page
Route: Route
Query: Query
Action: Action
Job: Job
Api: Api
ApiNamespace: ApiNamespace
Crud: Crud
}

export type GetDeclForType<T extends Decl['declType']> = Extract<
Decl,
{ declType: T }
>

// NOTE: Entities are defined in the schema.prisma file, but they can still be
// referenced.
export type DeclType = Decl['declType'] | 'Entity'

export type Page = {
component: ExtImport
authRequired?: boolean
authRequired: Optional<boolean>
}

export type Route = {
Expand All @@ -32,39 +45,39 @@ export type Route = {

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

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

export type Job = {
executor: JobExecutor
perform: Perform
schedule?: Schedule
entities?: Ref<'Entity'>[]
schedule: Optional<Schedule>
entities: Optional<Ref<'Entity'>[]>
}
export type Schedule = {
cron: string
args?: object
executorOptions?: ExecutorOptions
args: Optional<object>
executorOptions: Optional<ExecutorOptions>
}

export type Perform = {
fn: ExtImport
executorOptions?: ExecutorOptions
executorOptions: Optional<ExecutorOptions>
}

export type Api = {
fn: ExtImport
middlewareConfigFn?: ExtImport
entities?: Ref<'Entity'>[]
middlewareConfigFn: Optional<ExtImport>
entities: Optional<Ref<'Entity'>[]>
httpRoute: HttpRoute
auth?: boolean
auth: Optional<boolean>
}

export type ApiNamespace = {
Expand All @@ -80,13 +93,13 @@ export type Crud = {
export type App = {
wasp: Wasp
title: string
head?: string[]
auth?: Auth
server?: Server
client?: Client
db?: Db
emailSender?: EmailSender
webSocket?: WebSocket
head: Optional<string[]>
auth: Optional<Auth>
server: Optional<Server>
client: Optional<Client>
db: Optional<Db>
emailSender: Optional<EmailSender>
webSocket: Optional<WebSocket>
}

export type ExtImport = {
Expand All @@ -98,89 +111,87 @@ export type ExtImport = {
export type JobExecutor = 'PgBoss'

export type ExecutorOptions = {
pgBoss?: object
pgBoss: Optional<object>
}

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

export type HttpRoute = [HttpMethod, string]

export type CrudOperations = {
get?: CrudOperationOptions
getAll?: CrudOperationOptions
create?: CrudOperationOptions
update?: CrudOperationOptions
delete?: CrudOperationOptions
get: Optional<CrudOperationOptions>
getAll: Optional<CrudOperationOptions>
create: Optional<CrudOperationOptions>
update: Optional<CrudOperationOptions>
delete: Optional<CrudOperationOptions>
}

export type CrudOperationOptions = {
isPublic?: boolean
overrideFn?: ExtImport
isPublic: Optional<boolean>
overrideFn: Optional<ExtImport>
}

export type Wasp = {
// TODO: Check semver in export type system?
version: string
}

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

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

export type UsernameAndPasswordConfig = {
userSignupFields?: ExtImport
userSignupFields: Optional<ExtImport>
}

export type ExternalAuthConfig = {
configFn?: ExtImport
userSignupFields?: ExtImport
configFn: Optional<ExtImport>
userSignupFields: Optional<ExtImport>
}

export type EmailAuthConfig = {
userSignupFields?: ExtImport
userSignupFields: Optional<ExtImport>
fromField: EmailFromField
emailVerification: EmailVerificationConfig
passwordReset: PasswordResetConfig
}

export type EmailSender = {
provider: EmailProvider
defaultFrom?: EmailFromField
defaultFrom: Optional<EmailFromField>
}

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

export type EmailFromField = {
name?: string
name: Optional<string>
email: string
}

export type EmailVerificationConfig = {
getEmailContentFn?: ExtImport
getEmailContentFn: Optional<ExtImport>
clientRoute: Ref<'Route'>
}

export type PasswordResetConfig = {
getEmailContentFn?: ExtImport
getEmailContentFn: Optional<ExtImport>
clientRoute: Ref<'Route'>
}

Expand All @@ -190,21 +201,57 @@ export type Ref<T extends DeclType> = {
}

export type Server = {
setupFn?: ExtImport
middlewareConfigFn?: ExtImport
setupFn: Optional<ExtImport>
middlewareConfigFn: Optional<ExtImport>
}

export type Client = {
setupFn?: ExtImport
rootComponent?: ExtImport
baseDir?: `/${string}`
setupFn: Optional<ExtImport>
rootComponent: Optional<ExtImport>
baseDir: Optional<`/${string}`>
}

export type Db = {
seeds?: ExtImport[]
seeds: Optional<ExtImport[]>
}

export type WebSocket = {
fn: ExtImport
autoConnect?: boolean
autoConnect: Optional<boolean>
}

/**
* We use this type for fields that are optional (Maybe) in AppSpec.
* We do this instead of `someField?:` because we want TypeScript to force us
* to explicitly set the field to `undefined`.
*
* This way, if the AppSpec changes on the Haskell side, we won't forget to
* implement a proper mapping in TypeScript.
*
* For example, let's say `bar` is optional (both for the user and for the app
* spec). This would be the correct mapping code:
* ```
* const { foo, bar } = userConfig
* const decl: SomeDecl = {
* foo: mapForAppSpec(foo),
* bar: mapForAppSpec(bar)
* }
* ```
* The code below is wrong. It forgets to map `bar` even though it might exist
* in `userConfig`:
* ```
* const { foo } = userConfig
* const decl: SomeDecl = {
* foo: mapForAppSpec(foo),
* }
* ```
* If `bar` is an optional field of `SomeDecl` (`bar?: string`), TypeScript
* doesn't catch this error.
*
* If `bar` is a mandatory field of `SomeDecl` that can be set to `undefined`
* (`bar: Optional<string>`), TypeScript catches the error.
*
* Explicitly setting optional fields to `undefined` doesn't impact JSON
* serialization since fields set to `undefined` are treated as missing fields.
*/
type Optional<T> = T | undefined
Loading
Loading