From 31df276a259335352f1cf6ba1a35946d7ad304b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Sodi=C4=87?= Date: Thu, 24 Nov 2022 14:28:37 +0100 Subject: [PATCH] Client-side TypeScript support (#824) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add typescript support to generator * Change jsconfig to tsconfig for client * Change Main.js to Main.tsx * Implement TypeScript support on client * Fix formatting * Fixes in actions/index.ts * Remove extra types from comments * Update waspc/src/Wasp/Generator/ServerGenerator.hs Co-authored-by: Martin Šošić * Remove server-specific typescript stuff * Fix useQuery type * Add typeroots to client tsconfig * Improve TypeScript support on client * Remove tsx extension from template wasp file * Format code Co-authored-by: Martin Šošić --- .../src/Wasp/Cli/Command/CreateNewProject.hs | 2 +- .../src/client/{MainPage.js => MainPage.tsx} | 0 .../new/src/client/react-app-env.d.ts | 60 ++++++ .../client/{jsconfig.json => tsconfig.json} | 14 +- .../templates/react-app/src/actions/core.d.ts | 3 + .../src/actions/{index.js => index.ts} | 175 +++++++++--------- .../templates/react-app/src/queries/core.d.ts | 3 + .../react-app/src/queries/index.d.ts | 8 + .../templates/react-app/tsconfig.json | 25 +++ .../Wasp/Generator/ExternalCodeGenerator.hs | 27 ++- .../Generator/ExternalCodeGenerator/Js.hs | 6 +- waspc/src/Wasp/Generator/ServerGenerator.hs | 16 +- waspc/src/Wasp/Generator/WebAppGenerator.hs | 16 +- .../WebAppGenerator/OperationsGenerator.hs | 9 +- waspc/waspc.cabal | 4 +- 15 files changed, 252 insertions(+), 116 deletions(-) rename waspc/data/Cli/templates/new/src/client/{MainPage.js => MainPage.tsx} (100%) create mode 100644 waspc/data/Cli/templates/new/src/client/react-app-env.d.ts rename waspc/data/Cli/templates/new/src/client/{jsconfig.json => tsconfig.json} (73%) create mode 100644 waspc/data/Generator/templates/react-app/src/actions/core.d.ts rename waspc/data/Generator/templates/react-app/src/actions/{index.js => index.ts} (62%) create mode 100644 waspc/data/Generator/templates/react-app/src/queries/core.d.ts create mode 100644 waspc/data/Generator/templates/react-app/src/queries/index.d.ts create mode 100644 waspc/data/Generator/templates/react-app/tsconfig.json diff --git a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs index 113333e4d..cdefb06eb 100644 --- a/waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs +++ b/waspc/cli/src/Wasp/Cli/Command/CreateNewProject.hs @@ -99,7 +99,7 @@ writeMainWaspFile waspProjectDir (ProjectInfo projectName appName) = writeFile a "", "route RootRoute { path: \"/\", to: MainPage }", "page MainPage {", - " component: import Main from \"@client/MainPage.js\"", + " component: import Main from \"@client/MainPage\"", "}" ] diff --git a/waspc/data/Cli/templates/new/src/client/MainPage.js b/waspc/data/Cli/templates/new/src/client/MainPage.tsx similarity index 100% rename from waspc/data/Cli/templates/new/src/client/MainPage.js rename to waspc/data/Cli/templates/new/src/client/MainPage.tsx diff --git a/waspc/data/Cli/templates/new/src/client/react-app-env.d.ts b/waspc/data/Cli/templates/new/src/client/react-app-env.d.ts new file mode 100644 index 000000000..e80934ce3 --- /dev/null +++ b/waspc/data/Cli/templates/new/src/client/react-app-env.d.ts @@ -0,0 +1,60 @@ +declare module '*.avif' { + const src: string; + export default src; +} + +declare module '*.bmp' { + const src: string; + export default src; +} + +declare module '*.gif' { + const src: string; + export default src; +} + +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.webp' { + const src: string; + export default src; +} + +declare module '*.svg' { + import * as React from 'react'; + + export const ReactComponent: React.FunctionComponent & { title?: string }>; + + const src: string; + export default src; +} + +declare module '*.module.css' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.scss' { + const classes: { readonly [key: string]: string }; + export default classes; +} + +declare module '*.module.sass' { + const classes: { readonly [key: string]: string }; + export default classes; +} diff --git a/waspc/data/Cli/templates/new/src/client/jsconfig.json b/waspc/data/Cli/templates/new/src/client/tsconfig.json similarity index 73% rename from waspc/data/Cli/templates/new/src/client/jsconfig.json rename to waspc/data/Cli/templates/new/src/client/tsconfig.json index e9b5551fd..34c23b30c 100644 --- a/waspc/data/Cli/templates/new/src/client/jsconfig.json +++ b/waspc/data/Cli/templates/new/src/client/tsconfig.json @@ -1,5 +1,15 @@ { "compilerOptions": { + // JSX support + "jsx": "preserve", + // Enable default imports in TypeScript. + "esModuleInterop": true, + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, // The following settings enable IDE support in user-provided source files. // Editing them might break features like import autocompletion and // definition lookup. Don't change them unless you know what you're doing. @@ -22,6 +32,8 @@ // definitions. "node_modules/@types/*" ] - } + }, + // Correctly resolve types: https://www.typescriptlang.org/tsconfig#typeRoots + "typeRoots": ["../../.wasp/out/web-app/node_modules/@types"] } } diff --git a/waspc/data/Generator/templates/react-app/src/actions/core.d.ts b/waspc/data/Generator/templates/react-app/src/actions/core.d.ts new file mode 100644 index 000000000..1171c7e1b --- /dev/null +++ b/waspc/data/Generator/templates/react-app/src/actions/core.d.ts @@ -0,0 +1,3 @@ +export type Action = (args?: Input) => Promise; + +export function createAction(actionRoute: string, entitiesUsed: unknown[]): Action diff --git a/waspc/data/Generator/templates/react-app/src/actions/index.js b/waspc/data/Generator/templates/react-app/src/actions/index.ts similarity index 62% rename from waspc/data/Generator/templates/react-app/src/actions/index.js rename to waspc/data/Generator/templates/react-app/src/actions/index.ts index cf97f1926..85dba33ba 100644 --- a/waspc/data/Generator/templates/react-app/src/actions/index.js +++ b/waspc/data/Generator/templates/react-app/src/actions/index.ts @@ -1,93 +1,57 @@ import { + QueryClient, + QueryKey, useMutation, + UseMutationOptions, useQueryClient, } from '@tanstack/react-query' - -export { configureQueryClient } from '../queryClient' +import { Action } from './core'; /** * An options object passed into the `useAction` hook and used to enhance the * action with extra options. * - * @typedef {Object} ActionOptions - * @property {PublicOptimisticUpdateDefinition[]} optimisticUpdates */ +export type ActionOptions = { + optimisticUpdates: OptimisticUpdateDefinition[]; +} /** * A documented (public) way to define optimistic updates. - * - * @typedef {Object} PublicOptimisticUpdateDefinition - * @property {GetQuerySpecifier} querySpecifier - * @property {UpdateQuery} updateQuery */ +export type OptimisticUpdateDefinition = { + getQuerySpecifier: GetQuerySpecifier; + updateQuery: UpdateQuery; +} /** * A function that takes an item and returns a Wasp Query specifier. - * - * @callback GetQuerySpecifier - * @param {T} item - * @returns {QuerySpecifier} */ +export type GetQuerySpecifier = (item: Item) => QuerySpecifier /** * A function that takes an item and the previous state of the cache, and returns * the desired (new) state of the cache. - * - * @callback UpdateQuery - * @param {T} item - * @param {T[]} oldData - * @returns {T[]} */ +export type UpdateQuery = (item: ActionInput, oldData: CacheItem[]) => CacheItem[] /** * A public query specifier used for addressing Wasp queries. See our docs for details: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * - * @typedef {any[]} QuerySpecifier */ - -/** - * An internal (undocumented, private, desugared) way of defining optimistic updates. - * - * @typedef {Object} InternalOptimisticUpdateDefinition - * @property {GetQuerySpecifier} querySpecifier - * @property {UpdateQuery} updateQuery - */ - -/** - * An UpdateQuery function "instantiated" with a specific item. It only takes - * the current state of the cache and returns the desired (new) state of the - * cache. - * - * @callback SpecificUpdateQuery - * @param {any[]} oldData - */ - -/** - * A specific, "instantiated" optimistic update definition which contains a - * fully-constructed query key and a specific update function. - * - * @typedef {Object} SpecificOptimisticUpdateDefinition - * @property {QueryKey} queryKey - * @property {SpecificUpdateQuery} updateQuery -*/ - -/** - * An array React Query uses to address queries. See their docs for details: - * https://react-query-v3.tanstack.com/guides/query-keys#array-keys. - * - * @typedef {any[]} QueryKey - */ - +export type QuerySpecifier = any[] /** * A hook for adding extra behavior to a Wasp Action (e.g., optimistic updates). * * @param actionFn The Wasp Action you wish to enhance/decorate. - * @param {ActionOptions} actionOptions An options object for enhancing/decorating the given Action. + * @param actionOptions An options object for enhancing/decorating the given Action. * @returns A decorated Action with added behavior but an unchanged API. */ -export function useAction(actionFn, actionOptions) { +export function useAction( + actionFn: (item: ActionInput) => Promise, + actionOptions: ActionOptions +): typeof actionFn { const queryClient = useQueryClient(); let mutationFn = actionFn @@ -110,14 +74,49 @@ export function useAction(actionFn, actionOptions) { } /** - * Translates/Desugars a public optimistic update definition object into a definition object our - * system uses internally. - * - * @param {PublicOptimisticUpdateDefinition} publicOptimisticUpdateDefinition An optimistic update definition - * object that's a part of the public API: https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns {InternalOptimisticUpdateDefinition} An internally-used optimistic update definition object. + * An internal (undocumented, private, desugared) way of defining optimistic updates. */ -function translateToInternalDefinition(publicOptimisticUpdateDefinition) { +type InternalOptimisticUpdateDefinition = { + getQueryKey: (item: ActionInput) => QueryKey, + updateQuery: UpdateQuery; + +} + +/** + * An UpdateQuery function "instantiated" with a specific item. It only takes + * the current state of the cache and returns the desired (new) state of the + * cache. + */ +type SpecificUpdateQuery = (oldData: Item[]) => Item[] + +/** + * A specific, "instantiated" optimistic update definition which contains a + * fully-constructed query key and a specific update function. + */ +type SpecificOptimisticUpdateDefinition = { + queryKey: QueryKey; + updateQuery: SpecificUpdateQuery; +} + +type InternalAction = Action & { + internal( + item: Input, + optimisticUpdateDefinitions: SpecificOptimisticUpdateDefinition[] + ): Promise +} + +/** + * Translates/Desugars a public optimistic update definition object into a + * definition object our system uses internally. + * + * @param publicOptimisticUpdateDefinition An optimistic update definition + * object that's a part of the public API: + * https://wasp-lang.dev/docs/language/features#the-useaction-hook. + * @returns An internally-used optimistic update definition object. + */ +function translateToInternalDefinition( + publicOptimisticUpdateDefinition: OptimisticUpdateDefinition +): InternalOptimisticUpdateDefinition { const { getQuerySpecifier, updateQuery } = publicOptimisticUpdateDefinition const definitionErrors = [] @@ -141,17 +140,19 @@ function translateToInternalDefinition(publicOptimisticUpdateDefinition) { * Creates a function that performs an action while telling it about the * optimistic updates it caused. * - * @param actionFn - The Wasp Action. - * @param {InternalOptimisticUpdateDefinition} optimisticUpdateDefinitions - The optimisitc updates the - * action causes. - * @returns A + * @param actionFn The Wasp Action. + * @param optimisticUpdateDefinitions The optimisitc updates the action causes. + * @returns An decorated action which performs optimistic updates. */ -function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { +function makeOptimisticUpdateMutationFn( + actionFn: Action, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition[] +): typeof actionFn { return function performActionWithOptimisticUpdates(item) { const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( generalDefinition => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) ) - return actionFn.internal(item, specificOptimisticUpdateDefinitions) + return (actionFn as InternalAction).internal(item, specificOptimisticUpdateDefinitions) } } @@ -163,13 +164,18 @@ function makeOptimisticUpdateMutationFn(actionFn, optimisticUpdateDefinitions) { * optimistic updates definition. Check their docs for details: * https://tanstack.com/query/v4/docs/guides/optimistic-updates?from=reactQueryV3&original=https://react-query-v3.tanstack.com/guides/optimistic-updates * - * @param {Object} queryClient The QueryClient instance used by React Query. - * @param {InternalOptimisticUpdateDefinition} optimisticUpdateDefinitions A list containing internal optimistic updates definition objects - * (i.e., a list where each object carries the instructions for performing particular optimistic update). - * @returns {Object} An object containing 'onMutate' and 'onError' functions corresponding to the given optimistic update - * definitions (check the docs linked above for details). + * @param queryClient The QueryClient instance used by React Query. + * @param optimisticUpdateDefinitions A list containing internal optimistic + * updates definition objects (i.e., a list where each object carries the + * instructions for performing particular optimistic update). + * @returns An object containing 'onMutate' and 'onError' functions + * corresponding to the given optimistic update definitions (check the docs + * linked above for details). */ -function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) { +function makeRqOptimisticUpdateOptions( + queryClient: QueryClient, + optimisticUpdateDefinitions: InternalOptimisticUpdateDefinition[] +): Pick { async function onMutate(item) { const specificOptimisticUpdateDefinitions = optimisticUpdateDefinitions.map( generalDefinition => getOptimisticUpdateDefinitionForSpecificItem(generalDefinition, item) @@ -187,7 +193,7 @@ function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) const previousData = new Map() specificOptimisticUpdateDefinitions.forEach(({ queryKey, updateQuery }) => { // Snapshot the currently cached value. - const previousDataForQuery = queryClient.getQueryData(queryKey) + const previousDataForQuery: CacheItem[] = queryClient.getQueryData(queryKey) // Attempt to optimistically update the cache using the new value. try { @@ -227,13 +233,16 @@ function makeRqOptimisticUpdateOptions(queryClient, optimisticUpdateDefinitions) * uses a closure over the updated item to construct an item-specific query key * (e.g., useful when the query key depends on an ID). * - * @param {InternalOptimisticUpdateDefinition} optimisticUpdateDefinition The general, "uninstantiated" optimistic + * @param optimisticUpdateDefinition The general, "uninstantiated" optimistic * update definition with a function for constructing the query key. - * @param item The item triggering the Action/optimistic update (i.e., the argument passed to the Action). - * @returns {SpecificOptimisticUpdateDefinition} A specific optimistic update definition - * which corresponds to the provided definition and closes over the provided item. + * @param item The item triggering the Action/optimistic update (i.e., the + * argument passed to the Action). + * @returns A specific optimistic update definition which corresponds to the + * provided definition and closes over the provided item. */ -function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition, item) { +function getOptimisticUpdateDefinitionForSpecificItem( + optimisticUpdateDefinition: InternalOptimisticUpdateDefinition, item: ActionInput +): SpecificOptimisticUpdateDefinition { const { getQueryKey, updateQuery } = optimisticUpdateDefinition return { queryKey: getQueryKey(item), @@ -244,11 +253,11 @@ function getOptimisticUpdateDefinitionForSpecificItem(optimisticUpdateDefinition /** * Translates a Wasp query specifier to a query cache key used by React Query. * - * @param {QuerySpecifier} querySpecifier A query specifier that's a part of the public API: + * @param querySpecifier A query specifier that's a part of the public API: * https://wasp-lang.dev/docs/language/features#the-useaction-hook. - * @returns {QueryKey} A cache key React Query internally uses for addressing queries. + * @returns A cache key React Query internally uses for addressing queries. */ -function getRqQueryKeyFromSpecifier(querySpecifier) { +function getRqQueryKeyFromSpecifier(querySpecifier: QuerySpecifier): QueryKey { const [queryFn, ...otherKeys] = querySpecifier return [...queryFn.queryCacheKey, ...otherKeys] } diff --git a/waspc/data/Generator/templates/react-app/src/queries/core.d.ts b/waspc/data/Generator/templates/react-app/src/queries/core.d.ts new file mode 100644 index 000000000..edde495db --- /dev/null +++ b/waspc/data/Generator/templates/react-app/src/queries/core.d.ts @@ -0,0 +1,3 @@ +export type Query = (args: Input) => Promise + +export function createQuery(queryRoute: string, entitiesUsed: any[]): Query diff --git a/waspc/data/Generator/templates/react-app/src/queries/index.d.ts b/waspc/data/Generator/templates/react-app/src/queries/index.d.ts new file mode 100644 index 000000000..a331fa9d7 --- /dev/null +++ b/waspc/data/Generator/templates/react-app/src/queries/index.d.ts @@ -0,0 +1,8 @@ +import { UseQueryResult } from "@tanstack/react-query"; +import { OutputHTMLAttributes } from "react"; +import { Query } from "./core"; + +export function useQuery( + queryFn: Query, + queryFnArgs?: Input, options?: any +): UseQueryResult diff --git a/waspc/data/Generator/templates/react-app/tsconfig.json b/waspc/data/Generator/templates/react-app/tsconfig.json new file mode 100644 index 000000000..3fab473aa --- /dev/null +++ b/waspc/data/Generator/templates/react-app/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "es2018", + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "allowJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx" + }, + "include": [ + "src" + ] +} diff --git a/waspc/src/Wasp/Generator/ExternalCodeGenerator.hs b/waspc/src/Wasp/Generator/ExternalCodeGenerator.hs index 10a9d7656..1c6887d32 100644 --- a/waspc/src/Wasp/Generator/ExternalCodeGenerator.hs +++ b/waspc/src/Wasp/Generator/ExternalCodeGenerator.hs @@ -3,12 +3,14 @@ module Wasp.Generator.ExternalCodeGenerator ) where +import Data.Maybe (mapMaybe) import StrongPath (File', Path', Rel, ()) import qualified StrongPath as SP import qualified System.FilePath as FP import qualified Wasp.AppSpec.ExternalCode as EC import qualified Wasp.Generator.ExternalCodeGenerator.Common as C -import Wasp.Generator.ExternalCodeGenerator.Js (generateJsFile) +import Wasp.Generator.ExternalCodeGenerator.Js (genSourceFile) +import Wasp.Generator.FileDraft (FileDraft) import qualified Wasp.Generator.FileDraft as FD import Wasp.Generator.Monad (Generator) @@ -18,17 +20,22 @@ genExternalCodeDir :: C.ExternalCodeGeneratorStrategy -> [EC.File] -> Generator [FD.FileDraft] -genExternalCodeDir strategy = mapM (genFile strategy) +genExternalCodeDir strategy = sequence . mapMaybe (genFile strategy) -genFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> Generator FD.FileDraft +genFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> Maybe (Generator FD.FileDraft) genFile strategy file - | extension `elem` [".js", ".jsx"] = generateJsFile strategy file - | otherwise = - let relDstPath = C._extCodeDirInProjectRootDir strategy dstPathInGenExtCodeDir - absSrcPath = EC.fileAbsPath file - in return $ FD.createCopyFileDraft relDstPath absSrcPath + | fileName == "tsconfig.json" = Nothing + | extension `elem` [".js", ".jsx", ".ts", ".tsx"] = Just $ genSourceFile strategy file + | otherwise = Just $ genResourceFile strategy file where + extension = FP.takeExtension filePath + fileName = FP.takeFileName filePath + filePath = SP.toFilePath $ EC.filePathInExtCodeDir file + +genResourceFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> Generator FileDraft +genResourceFile strategy file = return $ FD.createCopyFileDraft relDstPath absSrcPath + where + relDstPath = C._extCodeDirInProjectRootDir strategy dstPathInGenExtCodeDir + absSrcPath = EC.fileAbsPath file dstPathInGenExtCodeDir :: Path' (Rel C.GeneratedExternalCodeDir) File' dstPathInGenExtCodeDir = C.castRelPathFromSrcToGenExtCodeDir $ EC.filePathInExtCodeDir file - - extension = FP.takeExtension $ SP.toFilePath $ EC.filePathInExtCodeDir file diff --git a/waspc/src/Wasp/Generator/ExternalCodeGenerator/Js.hs b/waspc/src/Wasp/Generator/ExternalCodeGenerator/Js.hs index ab8b6bc88..5c64ef2b5 100644 --- a/waspc/src/Wasp/Generator/ExternalCodeGenerator/Js.hs +++ b/waspc/src/Wasp/Generator/ExternalCodeGenerator/Js.hs @@ -1,5 +1,5 @@ module Wasp.Generator.ExternalCodeGenerator.Js - ( generateJsFile, + ( genSourceFile, resolveJsFileWaspImportsForExtCodeDir, ) where @@ -17,8 +17,8 @@ import qualified Wasp.Generator.ExternalCodeGenerator.Common as C import qualified Wasp.Generator.FileDraft as FD import Wasp.Generator.Monad (Generator) -generateJsFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> Generator FD.FileDraft -generateJsFile strategy file = return $ FD.createTextFileDraft dstPath text' +genSourceFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> Generator FD.FileDraft +genSourceFile strategy file = return $ FD.createTextFileDraft dstPath text' where filePathInSrcExtCodeDir = EC.filePathInExtCodeDir file diff --git a/waspc/src/Wasp/Generator/ServerGenerator.hs b/waspc/src/Wasp/Generator/ServerGenerator.hs index 57e2e089d..b34b3397d 100644 --- a/waspc/src/Wasp/Generator/ServerGenerator.hs +++ b/waspc/src/Wasp/Generator/ServerGenerator.hs @@ -56,7 +56,7 @@ import Wasp.Util ((<++>)) genServer :: AppSpec -> Generator [FileDraft] genServer spec = sequence - [ genReadme, + [ genFileCopy [relfile|README.md|], genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore @@ -68,6 +68,7 @@ genServer spec = <++> genJobs spec <++> genJobExecutors <++> genPatches spec + where genFileCopy = return . C.mkTmplFd genDotEnv :: AppSpec -> Generator [FileDraft] genDotEnv spec = return $ @@ -83,9 +84,6 @@ genDotEnv spec = return $ dotEnvInServerRootDir :: Path' (Rel C.ServerRootDir) File' dotEnvInServerRootDir = [relfile|.env|] -genReadme :: Generator FileDraft -genReadme = return $ C.mkTmplFd (C.asTmplFile [relfile|README.md|]) - genPackageJson :: AppSpec -> N.NpmDepsForWasp -> Generator FileDraft genPackageJson spec waspDependencies = do combinedDependencies <- N.genNpmDepsForPackage spec waspDependencies @@ -157,10 +155,10 @@ genGitignore = genSrcDir :: AppSpec -> Generator [FileDraft] genSrcDir spec = sequence - [ copyTmplFile [relfile|app.js|], - copyTmplFile [relfile|utils.js|], - copyTmplFile [relfile|core/AuthError.js|], - copyTmplFile [relfile|core/HttpError.js|], + [ genFileCopy [relfile|app.js|], + genFileCopy [relfile|utils.js|], + genFileCopy [relfile|core/AuthError.js|], + genFileCopy [relfile|core/HttpError.js|], genDbClient spec, genConfigFile spec, genServerJs spec @@ -170,7 +168,7 @@ genSrcDir spec = <++> genOperations spec <++> genAuth spec where - copyTmplFile = return . C.mkSrcTmplFd + genFileCopy = return . C.mkSrcTmplFd genDbClient :: AppSpec -> Generator FileDraft genDbClient spec = return $ C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData) diff --git a/waspc/src/Wasp/Generator/WebAppGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator.hs index f9272acc6..3a7c885cb 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator.hs @@ -43,7 +43,8 @@ import Wasp.Util ((<++>)) genWebApp :: AppSpec -> Generator [FileDraft] genWebApp spec = do sequence - [ genReadme, + [ genFileCopy [relfile|README.md|], + genFileCopy [relfile|tsconfig.json|], genPackageJson spec (npmDepsForWasp spec), genNpmrc, genGitignore, @@ -54,6 +55,8 @@ genWebApp spec = do <++> genExternalCodeDir extClientCodeGeneratorStrategy (AS.externalClientFiles spec) <++> genExternalCodeDir extSharedCodeGeneratorStrategy (AS.externalSharedFiles spec) <++> genDotEnv spec + where + genFileCopy = return . C.mkTmplFd genDotEnv :: AppSpec -> Generator [FileDraft] genDotEnv spec = return $ @@ -69,9 +72,6 @@ genDotEnv spec = return $ dotEnvInWebAppRootDir :: Path' (Rel C.WebAppRootDir) File' dotEnvInWebAppRootDir = [relfile|.env|] -genReadme :: Generator FileDraft -genReadme = return $ C.mkTmplFd $ C.asTmplFile [relfile|README.md|] - genPackageJson :: AppSpec -> N.NpmDepsForWasp -> Generator FileDraft genPackageJson spec waspDependencies = do combinedDependencies <- N.genNpmDepsForPackage spec waspDependencies @@ -114,7 +114,13 @@ npmDepsForWasp spec = -- See discussion here for more: https://github.com/wasp-lang/wasp/pull/621 N.waspDevDependencies = AS.Dependency.fromList - [] + [ -- TODO: Allow users to choose whether they want to use TypeScript + -- in their projects and install these dependencies accordingly. + ("typescript", "^4.8.4"), + ("@types/react", "^18.0.25"), + ("@types/react-dom", "^18.0.8"), + ("@types/react-router-dom", "^5.3.3") + ] } depsRequiredByTailwind :: AppSpec -> [AS.Dependency.Dependency] diff --git a/waspc/src/Wasp/Generator/WebAppGenerator/OperationsGenerator.hs b/waspc/src/Wasp/Generator/WebAppGenerator/OperationsGenerator.hs index 12f91916a..c5abffc21 100644 --- a/waspc/src/Wasp/Generator/WebAppGenerator/OperationsGenerator.hs +++ b/waspc/src/Wasp/Generator/WebAppGenerator/OperationsGenerator.hs @@ -40,15 +40,18 @@ genQueries spec = mapM (genQuery spec) (AS.getQueries spec) <++> return [ C.mkSrcTmplFd [relfile|queries/index.js|], - C.mkSrcTmplFd [relfile|queries/core.js|] + C.mkSrcTmplFd [relfile|queries/index.d.ts|], + C.mkSrcTmplFd [relfile|queries/core.js|], + C.mkSrcTmplFd [relfile|queries/core.d.ts|] ] genActions :: AppSpec -> Generator [FileDraft] genActions spec = mapM (genAction spec) (AS.getActions spec) <++> return - [ C.mkSrcTmplFd [relfile|actions/index.js|], - C.mkSrcTmplFd [relfile|actions/core.js|] + [ C.mkSrcTmplFd [relfile|actions/index.ts|], + C.mkSrcTmplFd [relfile|actions/core.js|], + C.mkSrcTmplFd [relfile|actions/core.d.ts|] ] genQuery :: AppSpec -> (String, AS.Query.Query) -> Generator FileDraft diff --git a/waspc/waspc.cabal b/waspc/waspc.cabal index 639cd21e2..2acdb9e3e 100644 --- a/waspc/waspc.cabal +++ b/waspc/waspc.cabal @@ -33,6 +33,7 @@ data-files: Generator/templates/server/npmrc Generator/templates/**/*.prisma Generator/templates/**/*.toml + Generator/templates/**/*.ts Generator/templates/**/*.json Generator/templates/**/*.ico Generator/templates/**/*.html @@ -41,9 +42,10 @@ data-files: Generator/templates/**/*.png Cli/bash-completion Cli/templates/**/*.css - Cli/templates/**/*.js Cli/templates/**/*.json Cli/templates/**/*.png + Cli/templates/**/*.ts + Cli/templates/**/*.tsx Cli/templates/new/.gitignore Cli/templates/new/.wasproot Cli/templates/new/src/.waspignore