mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-09-11 15:26:19 +03:00
Support for the client setup function (#595)
* Add clientSetup support to analyzer and tidy up generator * Add clientSetup support to generator * Add tests for client setup function and tidy up * Address PR comments for client setup function * Clean up server and web app generators * Document react query invalidation on logout * Update e2e tests after query client cache clean * Fix syntax error in exmaple app * Remove leftover todos * Fix formatting * Fix formatting * Fix tests for client setup function * Update e2e test * Fix todos in templates * Add docs for client setup * Fix docs typo * Fix docs typo * Fix docs typo * Fix docs typo * Update e2e test
This commit is contained in:
parent
a6f5a7d7b2
commit
59c714111e
@ -1,22 +1,25 @@
|
||||
import config from '../config.js'
|
||||
import { queryClient } from '../queryClient'
|
||||
import { removeQueries } from '../operations/resources'
|
||||
import api, { setAuthToken, handleApiError } from '../api.js'
|
||||
|
||||
const login = async (email, password) => {
|
||||
export default async function login(email, password) {
|
||||
try {
|
||||
const args = { email, password }
|
||||
const response = await api.post(config.apiUrl + '/auth/login', args)
|
||||
|
||||
setAuthToken(response.data.token)
|
||||
|
||||
// TODO(matija): Currently we are removing all the queries, but we should remove only
|
||||
// non-public, user-dependent queries - public queries are expected not to change in respect
|
||||
// to the currently logged in user.
|
||||
queryClient.removeQueries()
|
||||
|
||||
// This isn't really neccessary because we remove all private queries after
|
||||
// logout, but we do it to be extra safe.
|
||||
//
|
||||
// For example, in future versions, users might be able to get to an SPA
|
||||
// login page while there's an active session. This code will prevent data
|
||||
// leaks in such cases.
|
||||
//
|
||||
// TODO(filip): We are currently removing all the queries, but we should
|
||||
// remove only non-public, user-dependent queries - public queries are
|
||||
// expected not to change in respect to the currently logged in user.
|
||||
removeQueries()
|
||||
} catch (error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default login
|
||||
|
@ -1,17 +1,9 @@
|
||||
import { clearLocalStorage } from '../api.js'
|
||||
import { queryClient } from '../queryClient'
|
||||
import { invalidateAndRemoveQueries } from '../operations/resources'
|
||||
|
||||
const logout = () => {
|
||||
export default function logout() {
|
||||
clearLocalStorage()
|
||||
|
||||
// TODO(matija): We are currently invalidating all the queries, but we should invalidate only the
|
||||
// non-public, user-dependent ones.
|
||||
queryClient.invalidateQueries()
|
||||
|
||||
// TODO(matija): We are currently clearing all the queries, but we should clear only the
|
||||
// non-public, user-dependent ones.
|
||||
queryClient.clear()
|
||||
// TODO(filip): We are currently invalidating and removing all the queries, but
|
||||
// we should remove only the non-public, user-dependent ones.
|
||||
invalidateAndRemoveQueries()
|
||||
}
|
||||
|
||||
export default logout
|
||||
|
||||
|
@ -1,13 +1,10 @@
|
||||
import config from '../config.js'
|
||||
import api, { handleApiError } from '../api.js'
|
||||
|
||||
|
||||
const signup = async (userFields) => {
|
||||
export default async function signup(userFields) {
|
||||
try {
|
||||
await api.post(config.apiUrl + '/auth/signup', userFields)
|
||||
} catch (error) {
|
||||
handleApiError(error)
|
||||
}
|
||||
}
|
||||
|
||||
export default signup
|
||||
|
@ -2,7 +2,10 @@ import { useQuery } from '../queries'
|
||||
import config from '../config.js'
|
||||
import api, { handleApiError } from '../api.js'
|
||||
|
||||
const getMe = async () => {
|
||||
export default function useAuth(queryFnArgs, config) {
|
||||
return useQuery(getMe, queryFnArgs, config)
|
||||
}
|
||||
async function getMe() {
|
||||
try {
|
||||
const response = await api.get(config.apiUrl + '/auth/me')
|
||||
|
||||
@ -15,10 +18,5 @@ const getMe = async () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getMe.queryCacheKey = 'auth/me'
|
||||
|
||||
const useAuth = (queryFnArgs, config) => {
|
||||
return useQuery(getMe, queryFnArgs, config)
|
||||
}
|
||||
|
||||
export default useAuth
|
||||
|
@ -1,3 +1,4 @@
|
||||
{{={= =}=}}
|
||||
import React from 'react'
|
||||
import ReactDOM from 'react-dom'
|
||||
import { QueryClientProvider } from 'react-query'
|
||||
@ -6,17 +7,32 @@ import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
{=# doesClientSetupFnExist =}
|
||||
{=& clientSetupJsFnImportStatement =}
|
||||
{=/ doesClientSetupFnExist =}
|
||||
|
||||
import './index.css'
|
||||
|
||||
startApp()
|
||||
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
async function startApp() {
|
||||
{=# doesClientSetupFnExist =}
|
||||
await {= clientSetupJsFnIdentifier =}()
|
||||
{=/ doesClientSetupFnExist =}
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ const resourceToQueryCacheKeys = new Map()
|
||||
* @param {string} queryCacheKey - Unique key under used to identify query in the cache.
|
||||
* @param {string[]} resources - Names of resources that query is using.
|
||||
*/
|
||||
export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
for (const resource of resources) {
|
||||
let cacheKeys = resourceToQueryCacheKeys.get(resource)
|
||||
if (!cacheKeys) {
|
||||
@ -28,7 +28,7 @@ export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
* @param {string} resource - Resource name.
|
||||
* @returns {string[]} Array of "query cache keys" of queries that use specified resource.
|
||||
*/
|
||||
export const getQueriesUsingResource = (resource) => {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ export const getQueriesUsingResource = (resource) => {
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export const invalidateQueriesUsing = (resources) => {
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
@ -45,3 +45,17 @@ export const invalidateQueriesUsing = (resources) => {
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
queryClient.invalidateQueries()
|
||||
// If we don't remove the queries after invalidating them, the old query data
|
||||
// remains in the cache, casuing a potential privacy issue.
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"030bfc5cd200d732eaca398b298057c148c3f9d9b8a3e7a24918db438b5d1861"
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,7 +312,7 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"35f14c763398a5acdd12deea700cc124bca6393553696f2764c2ebfab0609ee1"
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -6,17 +6,26 @@ import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
import './index.css'
|
||||
|
||||
startApp()
|
||||
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
async function startApp() {
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const resourceToQueryCacheKeys = new Map()
|
||||
* @param {string} queryCacheKey - Unique key under used to identify query in the cache.
|
||||
* @param {string[]} resources - Names of resources that query is using.
|
||||
*/
|
||||
export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
for (const resource of resources) {
|
||||
let cacheKeys = resourceToQueryCacheKeys.get(resource)
|
||||
if (!cacheKeys) {
|
||||
@ -27,7 +27,7 @@ export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
* @param {string} resource - Resource name.
|
||||
* @returns {string[]} Array of "query cache keys" of queries that use specified resource.
|
||||
*/
|
||||
export const getQueriesUsingResource = (resource) => {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ export const getQueriesUsingResource = (resource) => {
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export const invalidateQueriesUsing = (resources) => {
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
@ -44,3 +44,17 @@ export const invalidateQueriesUsing = (resources) => {
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
queryClient.invalidateQueries()
|
||||
// If we don't remove the queries after invalidating them, the old query data
|
||||
// remains in the cache, casuing a potential privacy issue.
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"030bfc5cd200d732eaca398b298057c148c3f9d9b8a3e7a24918db438b5d1861"
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,7 +312,7 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"35f14c763398a5acdd12deea700cc124bca6393553696f2764c2ebfab0609ee1"
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -6,17 +6,26 @@ import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
import './index.css'
|
||||
|
||||
startApp()
|
||||
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
async function startApp() {
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const resourceToQueryCacheKeys = new Map()
|
||||
* @param {string} queryCacheKey - Unique key under used to identify query in the cache.
|
||||
* @param {string[]} resources - Names of resources that query is using.
|
||||
*/
|
||||
export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
for (const resource of resources) {
|
||||
let cacheKeys = resourceToQueryCacheKeys.get(resource)
|
||||
if (!cacheKeys) {
|
||||
@ -27,7 +27,7 @@ export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
* @param {string} resource - Resource name.
|
||||
* @returns {string[]} Array of "query cache keys" of queries that use specified resource.
|
||||
*/
|
||||
export const getQueriesUsingResource = (resource) => {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ export const getQueriesUsingResource = (resource) => {
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export const invalidateQueriesUsing = (resources) => {
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
@ -44,3 +44,17 @@ export const invalidateQueriesUsing = (resources) => {
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
queryClient.invalidateQueries()
|
||||
// If we don't remove the queries after invalidating them, the old query data
|
||||
// remains in the cache, casuing a potential privacy issue.
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
@ -312,7 +312,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"030bfc5cd200d732eaca398b298057c148c3f9d9b8a3e7a24918db438b5d1861"
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -333,7 +333,7 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"35f14c763398a5acdd12deea700cc124bca6393553696f2764c2ebfab0609ee1"
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -6,17 +6,26 @@ import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
import './index.css'
|
||||
|
||||
startApp()
|
||||
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
async function startApp() {
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const resourceToQueryCacheKeys = new Map()
|
||||
* @param {string} queryCacheKey - Unique key under used to identify query in the cache.
|
||||
* @param {string[]} resources - Names of resources that query is using.
|
||||
*/
|
||||
export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
for (const resource of resources) {
|
||||
let cacheKeys = resourceToQueryCacheKeys.get(resource)
|
||||
if (!cacheKeys) {
|
||||
@ -27,7 +27,7 @@ export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
* @param {string} resource - Resource name.
|
||||
* @returns {string[]} Array of "query cache keys" of queries that use specified resource.
|
||||
*/
|
||||
export const getQueriesUsingResource = (resource) => {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ export const getQueriesUsingResource = (resource) => {
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export const invalidateQueriesUsing = (resources) => {
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
@ -44,3 +44,17 @@ export const invalidateQueriesUsing = (resources) => {
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
queryClient.invalidateQueries()
|
||||
// If we don't remove the queries after invalidating them, the old query data
|
||||
// remains in the cache, casuing a potential privacy issue.
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
@ -291,7 +291,7 @@
|
||||
"file",
|
||||
"web-app/src/index.js"
|
||||
],
|
||||
"030bfc5cd200d732eaca398b298057c148c3f9d9b8a3e7a24918db438b5d1861"
|
||||
"a477184a6e6e7cb33e056febc8a4a1e1132ecc697763855fa2af07cf02c79908"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -312,7 +312,7 @@
|
||||
"file",
|
||||
"web-app/src/operations/resources.js"
|
||||
],
|
||||
"35f14c763398a5acdd12deea700cc124bca6393553696f2764c2ebfab0609ee1"
|
||||
"4772a216b647ace3eedace9b02897922725b58131031d21be59187b1652ccade"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -6,17 +6,26 @@ import router from './router'
|
||||
import { queryClient } from './queryClient'
|
||||
import * as serviceWorker from './serviceWorker'
|
||||
|
||||
|
||||
import './index.css'
|
||||
|
||||
startApp()
|
||||
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
async function startApp() {
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister();
|
||||
render()
|
||||
|
||||
// If you want your app to work offline and load faster, you can change
|
||||
// unregister() to register() below. Note this comes with some pitfalls.
|
||||
// Learn more about service workers: https://bit.ly/CRA-PWA
|
||||
serviceWorker.unregister()
|
||||
}
|
||||
|
||||
function render() {
|
||||
ReactDOM.render(
|
||||
<QueryClientProvider client={queryClient}>
|
||||
{ router }
|
||||
</QueryClientProvider>,
|
||||
document.getElementById('root')
|
||||
)
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ const resourceToQueryCacheKeys = new Map()
|
||||
* @param {string} queryCacheKey - Unique key under used to identify query in the cache.
|
||||
* @param {string[]} resources - Names of resources that query is using.
|
||||
*/
|
||||
export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
export function addResourcesUsedByQuery(queryCacheKey, resources) {
|
||||
for (const resource of resources) {
|
||||
let cacheKeys = resourceToQueryCacheKeys.get(resource)
|
||||
if (!cacheKeys) {
|
||||
@ -27,7 +27,7 @@ export const addResourcesUsedByQuery = (queryCacheKey, resources) => {
|
||||
* @param {string} resource - Resource name.
|
||||
* @returns {string[]} Array of "query cache keys" of queries that use specified resource.
|
||||
*/
|
||||
export const getQueriesUsingResource = (resource) => {
|
||||
export function getQueriesUsingResource(resource) {
|
||||
return Array.from(resourceToQueryCacheKeys.get(resource) || [])
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ export const getQueriesUsingResource = (resource) => {
|
||||
* Invalidates all queries that are using specified resources.
|
||||
* @param {string[]} resources - Names of resources.
|
||||
*/
|
||||
export const invalidateQueriesUsing = (resources) => {
|
||||
export function invalidateQueriesUsing(resources) {
|
||||
const queryCacheKeysToInvalidate = new Set()
|
||||
for (const resource of resources) {
|
||||
getQueriesUsingResource(resource).forEach(key => queryCacheKeysToInvalidate.add(key))
|
||||
@ -44,3 +44,17 @@ export const invalidateQueriesUsing = (resources) => {
|
||||
queryClient.invalidateQueries(queryCacheKey)
|
||||
}
|
||||
}
|
||||
|
||||
export function removeQueries() {
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
||||
export function invalidateAndRemoveQueries() {
|
||||
// If we don't invalidate the queries before removing them, Wasp will stay on
|
||||
// the same page. The user would have to manually refresh the page to "finish"
|
||||
// logging out.
|
||||
queryClient.invalidateQueries()
|
||||
// If we don't remove the queries after invalidating them, the old query data
|
||||
// remains in the cache, casuing a potential privacy issue.
|
||||
queryClient.removeQueries()
|
||||
}
|
||||
|
3
waspc/examples/todoApp/ext/clientSetup.js
Normal file
3
waspc/examples/todoApp/ext/clientSetup.js
Normal file
@ -0,0 +1,3 @@
|
||||
export default function setup() {
|
||||
console.log("This was called from the client setup function")
|
||||
}
|
@ -15,6 +15,9 @@ app todoApp {
|
||||
server: {
|
||||
setupFn: import setup from "@ext/serverSetup.js"
|
||||
},
|
||||
client: {
|
||||
setupFn: import setup from "@ext/clientSetup.js"
|
||||
},
|
||||
db: {
|
||||
system: PostgreSQL
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ module Wasp.AppSpec.App (App (..)) where
|
||||
|
||||
import Data.Data (Data)
|
||||
import Wasp.AppSpec.App.Auth (Auth)
|
||||
import Wasp.AppSpec.App.Client (Client)
|
||||
import Wasp.AppSpec.App.Db (Db)
|
||||
import Wasp.AppSpec.App.Dependency (Dependency)
|
||||
import Wasp.AppSpec.App.Server (Server)
|
||||
@ -14,6 +15,7 @@ data App = App
|
||||
head :: Maybe [String],
|
||||
auth :: Maybe Auth,
|
||||
server :: Maybe Server,
|
||||
client :: Maybe Client,
|
||||
db :: Maybe Db,
|
||||
dependencies :: Maybe [Dependency]
|
||||
}
|
||||
|
14
waspc/src/Wasp/AppSpec/App/Client.hs
Normal file
14
waspc/src/Wasp/AppSpec/App/Client.hs
Normal file
@ -0,0 +1,14 @@
|
||||
{-# LANGUAGE DeriveDataTypeable #-}
|
||||
|
||||
module Wasp.AppSpec.App.Client
|
||||
( Client (..),
|
||||
)
|
||||
where
|
||||
|
||||
import Data.Data (Data)
|
||||
import Wasp.AppSpec.ExtImport (ExtImport)
|
||||
|
||||
data Client = Client
|
||||
{ setupFn :: Maybe ExtImport
|
||||
}
|
||||
deriving (Show, Eq, Data)
|
@ -42,11 +42,6 @@ import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||
import Wasp.Generator.Monad (Generator)
|
||||
import qualified Wasp.Generator.NpmDependencies as N
|
||||
import Wasp.Generator.ServerGenerator.AuthG (genAuth)
|
||||
import Wasp.Generator.ServerGenerator.Common
|
||||
( ServerSrcDir,
|
||||
asServerFile,
|
||||
asTmplFile,
|
||||
)
|
||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||
import Wasp.Generator.ServerGenerator.ConfigG (genConfigFile)
|
||||
import qualified Wasp.Generator.ServerGenerator.ExternalCodeGenerator as ServerExternalCodeGenerator
|
||||
@ -86,15 +81,15 @@ dotEnvInServerRootDir :: Path' (Rel C.ServerRootDir) File'
|
||||
dotEnvInServerRootDir = [relfile|.env|]
|
||||
|
||||
genReadme :: Generator FileDraft
|
||||
genReadme = return $ C.mkTmplFd (asTmplFile [relfile|README.md|])
|
||||
genReadme = return $ C.mkTmplFd (C.asTmplFile [relfile|README.md|])
|
||||
|
||||
genPackageJson :: AppSpec -> N.NpmDepsForWasp -> Generator FileDraft
|
||||
genPackageJson spec waspDependencies = do
|
||||
combinedDependencies <- N.genNpmDepsForPackage spec waspDependencies
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|package.json|])
|
||||
(asServerFile [relfile|package.json|])
|
||||
(C.asTmplFile [relfile|package.json|])
|
||||
(C.asServerFile [relfile|package.json|])
|
||||
( Just $
|
||||
object
|
||||
[ "depsChunk" .= N.getDependenciesPackageJsonEntry combinedDependencies,
|
||||
@ -137,16 +132,16 @@ genNpmrc :: Generator FileDraft
|
||||
genNpmrc =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|npmrc|])
|
||||
(asServerFile [relfile|.npmrc|])
|
||||
(C.asTmplFile [relfile|npmrc|])
|
||||
(C.asServerFile [relfile|.npmrc|])
|
||||
Nothing
|
||||
|
||||
genNvmrc :: Generator FileDraft
|
||||
genNvmrc =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|nvmrc|])
|
||||
(asServerFile [relfile|.nvmrc|])
|
||||
(C.asTmplFile [relfile|nvmrc|])
|
||||
(C.asServerFile [relfile|.nvmrc|])
|
||||
-- We want to specify only the major version here. If we specified the
|
||||
-- entire version string (i.e., 16.0.0), our project would work only with
|
||||
-- that exact version, which we don't want. Unfortunately, the nvmrc file
|
||||
@ -158,17 +153,17 @@ genGitignore :: Generator FileDraft
|
||||
genGitignore =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|gitignore|])
|
||||
(asServerFile [relfile|.gitignore|])
|
||||
(C.asTmplFile [relfile|gitignore|])
|
||||
(C.asServerFile [relfile|.gitignore|])
|
||||
Nothing
|
||||
|
||||
genSrcDir :: AppSpec -> Generator [FileDraft]
|
||||
genSrcDir spec =
|
||||
sequence
|
||||
[ return $ C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|app.js|],
|
||||
return $ C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|utils.js|],
|
||||
return $ C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|core/AuthError.js|],
|
||||
return $ C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|core/HttpError.js|],
|
||||
[ copyTmplFile [relfile|app.js|],
|
||||
copyTmplFile [relfile|utils.js|],
|
||||
copyTmplFile [relfile|core/AuthError.js|],
|
||||
copyTmplFile [relfile|core/HttpError.js|],
|
||||
genDbClient spec,
|
||||
genConfigFile spec,
|
||||
genServerJs spec
|
||||
@ -177,6 +172,8 @@ genSrcDir spec =
|
||||
<++> genOperationsRoutes spec
|
||||
<++> genOperations spec
|
||||
<++> genAuth spec
|
||||
where
|
||||
copyTmplFile = return . C.mkSrcTmplFd
|
||||
|
||||
genDbClient :: AppSpec -> Generator FileDraft
|
||||
genDbClient spec = return $ C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||
@ -200,8 +197,8 @@ genServerJs :: AppSpec -> Generator FileDraft
|
||||
genServerJs spec =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|src/server.js|])
|
||||
(asServerFile [relfile|src/server.js|])
|
||||
(C.asTmplFile [relfile|src/server.js|])
|
||||
(C.asServerFile [relfile|src/server.js|])
|
||||
( Just $
|
||||
object
|
||||
[ "doesServerSetupFnExist" .= isJust maybeSetupJsFunction,
|
||||
@ -217,7 +214,7 @@ genServerJs spec =
|
||||
(fst <$> maybeSetupJsFnImportDetails, snd <$> maybeSetupJsFnImportDetails)
|
||||
|
||||
-- | TODO: Make this not hardcoded!
|
||||
relPosixPathFromSrcDirToExtSrcDir :: Path Posix (Rel (Dir ServerSrcDir)) (Dir GeneratedExternalCodeDir)
|
||||
relPosixPathFromSrcDirToExtSrcDir :: Path Posix (Rel (Dir C.ServerSrcDir)) (Dir GeneratedExternalCodeDir)
|
||||
relPosixPathFromSrcDirToExtSrcDir = [reldirP|./ext-src|]
|
||||
|
||||
genRoutesDir :: AppSpec -> Generator [FileDraft]
|
||||
@ -226,8 +223,8 @@ genRoutesDir spec =
|
||||
-- but I did not bother with it yet since it is used only here for now.
|
||||
return
|
||||
[ C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|src/routes/index.js|])
|
||||
(asServerFile [relfile|src/routes/index.js|])
|
||||
(C.asTmplFile [relfile|src/routes/index.js|])
|
||||
(C.asServerFile [relfile|src/routes/index.js|])
|
||||
( Just $
|
||||
object
|
||||
[ "operationsRouteInRootRouter" .= (operationsRouteInRootRouter :: String),
|
||||
|
@ -44,8 +44,6 @@ asServerFile = SP.castRel
|
||||
asServerSrcFile :: Path' (Rel d) File' -> Path' (Rel ServerSrcDir) File'
|
||||
asServerSrcFile = SP.castRel
|
||||
|
||||
-- * Paths
|
||||
|
||||
-- | Path where server root dir is generated.
|
||||
serverRootDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir ServerRootDir)
|
||||
serverRootDirInProjectRootDir = [reldir|server|]
|
||||
@ -57,8 +55,6 @@ serverSrcDirInServerRootDir = [reldir|src|]
|
||||
serverSrcDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir ServerSrcDir)
|
||||
serverSrcDirInProjectRootDir = serverRootDirInProjectRootDir </> serverSrcDirInServerRootDir
|
||||
|
||||
-- * Templates
|
||||
|
||||
mkTmplFd :: Path' (Rel ServerTemplatesDir) File' -> FileDraft
|
||||
mkTmplFd srcPath = mkTmplFdWithDstAndData srcPath dstPath Nothing
|
||||
where
|
||||
|
@ -6,34 +6,35 @@ where
|
||||
|
||||
import Data.Aeson (object, (.=))
|
||||
import Data.List (intercalate)
|
||||
import Data.Maybe (fromMaybe, isJust)
|
||||
import StrongPath
|
||||
( Dir,
|
||||
Path',
|
||||
Path,
|
||||
Posix,
|
||||
Rel,
|
||||
reldir,
|
||||
relfile,
|
||||
(</>),
|
||||
)
|
||||
import StrongPath.TH (reldirP)
|
||||
import Wasp.AppSpec (AppSpec)
|
||||
import qualified Wasp.AppSpec as AS
|
||||
import qualified Wasp.AppSpec.App as AS.App
|
||||
import Wasp.AppSpec.App.Client as AS.App.Client
|
||||
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||
import Wasp.AppSpec.Valid (getApp)
|
||||
import Wasp.Generator.Common (nodeVersion, nodeVersionBounds, npmVersionBounds)
|
||||
import Wasp.Generator.ExternalCodeGenerator (genExternalCodeDir)
|
||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||
import Wasp.Generator.FileDraft
|
||||
import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||
import Wasp.Generator.Monad (Generator)
|
||||
import qualified Wasp.Generator.NpmDependencies as N
|
||||
import qualified Wasp.Generator.WebAppGenerator.AuthG as AuthG
|
||||
import Wasp.Generator.WebAppGenerator.Common
|
||||
( asTmplFile,
|
||||
asWebAppFile,
|
||||
asWebAppSrcFile,
|
||||
)
|
||||
import Wasp.Generator.WebAppGenerator.AuthG (genAuth)
|
||||
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
||||
import qualified Wasp.Generator.WebAppGenerator.ExternalCodeGenerator as WebAppExternalCodeGenerator
|
||||
import Wasp.Generator.WebAppGenerator.OperationsGenerator (genOperations)
|
||||
import qualified Wasp.Generator.WebAppGenerator.RouterGenerator as RouterGenerator
|
||||
import Wasp.Generator.WebAppGenerator.RouterGenerator (genRouter)
|
||||
import qualified Wasp.SemanticVersion as SV
|
||||
import Wasp.Util ((<++>))
|
||||
|
||||
@ -45,14 +46,14 @@ genWebApp spec = do
|
||||
genNpmrc,
|
||||
genNvmrc,
|
||||
genGitignore,
|
||||
return $ C.mkTmplFd $ asTmplFile [relfile|netlify.toml|]
|
||||
return $ C.mkTmplFd $ C.asTmplFile [relfile|netlify.toml|]
|
||||
]
|
||||
<++> genPublicDir spec
|
||||
<++> genSrcDir spec
|
||||
<++> genExternalCodeDir WebAppExternalCodeGenerator.generatorStrategy (AS.externalCodeFiles spec)
|
||||
|
||||
genReadme :: Generator FileDraft
|
||||
genReadme = return $ C.mkTmplFd $ asTmplFile [relfile|README.md|]
|
||||
genReadme = return $ C.mkTmplFd $ C.asTmplFile [relfile|README.md|]
|
||||
|
||||
genPackageJson :: AppSpec -> N.NpmDepsForWasp -> Generator FileDraft
|
||||
genPackageJson spec waspDependencies = do
|
||||
@ -75,16 +76,16 @@ genNpmrc :: Generator FileDraft
|
||||
genNpmrc =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|npmrc|])
|
||||
(asWebAppFile [relfile|.npmrc|])
|
||||
(C.asTmplFile [relfile|npmrc|])
|
||||
(C.asWebAppFile [relfile|.npmrc|])
|
||||
Nothing
|
||||
|
||||
genNvmrc :: Generator FileDraft
|
||||
genNvmrc =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|nvmrc|])
|
||||
(asWebAppFile [relfile|.nvmrc|])
|
||||
(C.asTmplFile [relfile|nvmrc|])
|
||||
(C.asWebAppFile [relfile|.nvmrc|])
|
||||
-- We want to specify only the major version, check the comment in `ServerGenerator.hs` for details
|
||||
(Just (object ["nodeVersion" .= show (SV.major nodeVersion)]))
|
||||
|
||||
@ -111,17 +112,17 @@ genGitignore :: Generator FileDraft
|
||||
genGitignore =
|
||||
return $
|
||||
C.mkTmplFdWithDst
|
||||
(asTmplFile [relfile|gitignore|])
|
||||
(asWebAppFile [relfile|.gitignore|])
|
||||
(C.asTmplFile [relfile|gitignore|])
|
||||
(C.asWebAppFile [relfile|.gitignore|])
|
||||
|
||||
genPublicDir :: AppSpec -> Generator [FileDraft]
|
||||
genPublicDir spec = do
|
||||
publicIndexHtmlFd <- genPublicIndexHtml spec
|
||||
return $
|
||||
C.mkTmplFd (asTmplFile [relfile|public/favicon.ico|]) :
|
||||
C.mkTmplFd (C.asTmplFile [relfile|public/favicon.ico|]) :
|
||||
publicIndexHtmlFd :
|
||||
( let tmplData = object ["appName" .= (fst (getApp spec) :: String)]
|
||||
processPublicTmpl path = C.mkTmplFdWithData (asTmplFile $ [reldir|public|] </> path) tmplData
|
||||
processPublicTmpl path = C.mkTmplFdWithData (C.asTmplFile $ [reldir|public|] </> path) tmplData
|
||||
in processPublicTmpl
|
||||
<$> [ [relfile|manifest.json|]
|
||||
]
|
||||
@ -131,7 +132,7 @@ genPublicIndexHtml :: AppSpec -> Generator FileDraft
|
||||
genPublicIndexHtml spec =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|public/index.html|])
|
||||
(C.asTmplFile [relfile|public/index.html|])
|
||||
targetPath
|
||||
(Just templateData)
|
||||
where
|
||||
@ -142,48 +143,51 @@ genPublicIndexHtml spec =
|
||||
"head" .= (maybe "" (intercalate "\n") (AS.App.head $ snd $ getApp spec) :: String)
|
||||
]
|
||||
|
||||
-- * Src dir
|
||||
|
||||
srcDir :: Path' (Rel C.WebAppRootDir) (Dir C.WebAppSrcDir)
|
||||
srcDir = C.webAppSrcDirInWebAppRootDir
|
||||
|
||||
-- TODO(matija): Currently we also generate auth-specific parts in this file (e.g. token management),
|
||||
-- although they are not used anywhere outside.
|
||||
-- We could further "templatize" this file so only what is needed is generated.
|
||||
--
|
||||
|
||||
-- | Generates api.js file which contains token management and configured api (e.g. axios) instance.
|
||||
genApi :: FileDraft
|
||||
genApi = C.mkTmplFd (C.asTmplFile [relfile|src/api.js|])
|
||||
|
||||
genSrcDir :: AppSpec -> Generator [FileDraft]
|
||||
genSrcDir spec = do
|
||||
routerFd <- RouterGenerator.generateRouter spec
|
||||
operationsFds <- genOperations spec
|
||||
authFds <- AuthG.genAuth spec
|
||||
|
||||
return $
|
||||
generateLogo :
|
||||
routerFd :
|
||||
genApi :
|
||||
map
|
||||
processSrcTmpl
|
||||
[ [relfile|index.js|],
|
||||
[relfile|index.css|],
|
||||
[relfile|serviceWorker.js|],
|
||||
[relfile|config.js|],
|
||||
[relfile|queryClient.js|],
|
||||
[relfile|utils.js|]
|
||||
]
|
||||
++ operationsFds
|
||||
++ authFds
|
||||
genSrcDir spec =
|
||||
sequence
|
||||
[ copyTmplFile [relfile|index.css|],
|
||||
copyTmplFile [relfile|logo.png|],
|
||||
copyTmplFile [relfile|serviceWorker.js|],
|
||||
copyTmplFile [relfile|config.js|],
|
||||
copyTmplFile [relfile|queryClient.js|],
|
||||
copyTmplFile [relfile|utils.js|],
|
||||
genRouter spec,
|
||||
genIndexJs spec,
|
||||
genApi
|
||||
]
|
||||
<++> genOperations spec
|
||||
<++> genAuth spec
|
||||
where
|
||||
generateLogo =
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile [relfile|src/logo.png|])
|
||||
(srcDir </> asWebAppSrcFile [relfile|logo.png|])
|
||||
Nothing
|
||||
processSrcTmpl path =
|
||||
C.mkTmplFdWithDst
|
||||
(asTmplFile $ [reldir|src|] </> path)
|
||||
(srcDir </> asWebAppSrcFile path)
|
||||
copyTmplFile = return . C.mkSrcTmplFd
|
||||
|
||||
-- | Generates api.js file which contains token management and configured api (e.g. axios) instance.
|
||||
genApi :: Generator FileDraft
|
||||
genApi = return $ C.mkTmplFd (C.asTmplFile [relfile|src/api.js|])
|
||||
|
||||
genIndexJs :: AppSpec -> Generator FileDraft
|
||||
genIndexJs spec =
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(C.asTmplFile [relfile|src/index.js|])
|
||||
(C.asWebAppFile [relfile|src/index.js|])
|
||||
( Just $
|
||||
object
|
||||
[ "doesClientSetupFnExist" .= isJust maybeSetupJsFunction,
|
||||
"clientSetupJsFnImportStatement" .= fromMaybe "" maybeSetupJsFnImportStmt,
|
||||
"clientSetupJsFnIdentifier" .= fromMaybe "" maybeSetupJsFnImportIdentifier
|
||||
]
|
||||
)
|
||||
where
|
||||
maybeSetupJsFunction = AS.App.Client.setupFn =<< AS.App.client (snd $ getApp spec)
|
||||
maybeSetupJsFnImportDetails = getJsImportDetailsForExtFnImport relPosixPathFromSrcDirToExtSrcDir <$> maybeSetupJsFunction
|
||||
(maybeSetupJsFnImportIdentifier, maybeSetupJsFnImportStmt) =
|
||||
(fst <$> maybeSetupJsFnImportDetails, snd <$> maybeSetupJsFnImportDetails)
|
||||
|
||||
relPosixPathFromSrcDirToExtSrcDir :: Path Posix (Rel (Dir C.WebAppSrcDir)) (Dir GeneratedExternalCodeDir)
|
||||
relPosixPathFromSrcDirToExtSrcDir = [reldirP|./ext-src|]
|
||||
|
@ -1,6 +1,7 @@
|
||||
module Wasp.Generator.WebAppGenerator.Common
|
||||
( webAppRootDirInProjectRootDir,
|
||||
webAppSrcDirInWebAppRootDir,
|
||||
mkSrcTmplFd,
|
||||
mkTmplFd,
|
||||
mkTmplFdWithDst,
|
||||
mkTmplFdWithData,
|
||||
@ -13,6 +14,7 @@ module Wasp.Generator.WebAppGenerator.Common
|
||||
WebAppRootDir,
|
||||
WebAppSrcDir,
|
||||
WebAppTemplatesDir,
|
||||
WebAppTemplatesSrcDir,
|
||||
)
|
||||
where
|
||||
|
||||
@ -29,6 +31,8 @@ data WebAppSrcDir
|
||||
|
||||
data WebAppTemplatesDir
|
||||
|
||||
data WebAppTemplatesSrcDir
|
||||
|
||||
asTmplFile :: Path' (Rel d) File' -> Path' (Rel WebAppTemplatesDir) File'
|
||||
asTmplFile = SP.castRel
|
||||
|
||||
@ -38,8 +42,6 @@ asWebAppFile = SP.castRel
|
||||
asWebAppSrcFile :: Path' (Rel d) File' -> Path' (Rel WebAppSrcDir) File'
|
||||
asWebAppSrcFile = SP.castRel
|
||||
|
||||
-- * Paths
|
||||
|
||||
-- | Path where web app root dir is generated, relative to the root directory of the whole generated project.
|
||||
webAppRootDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir WebAppRootDir)
|
||||
webAppRootDirInProjectRootDir = [reldir|web-app|]
|
||||
@ -48,15 +50,26 @@ webAppRootDirInProjectRootDir = [reldir|web-app|]
|
||||
webAppSrcDirInWebAppRootDir :: Path' (Rel WebAppRootDir) (Dir WebAppSrcDir)
|
||||
webAppSrcDirInWebAppRootDir = [reldir|src|]
|
||||
|
||||
-- | Path to generated web app src/ directory, relative to the root directory of the whole generated project.
|
||||
webAppSrcDirInProjectRootDir :: Path' (Rel ProjectRootDir) (Dir WebAppSrcDir)
|
||||
webAppSrcDirInProjectRootDir = webAppRootDirInProjectRootDir </> webAppSrcDirInWebAppRootDir
|
||||
|
||||
-- * Templates
|
||||
|
||||
-- | Path in templates directory where web app templates reside.
|
||||
webAppTemplatesDirInTemplatesDir :: Path' (Rel TemplatesDir) (Dir WebAppTemplatesDir)
|
||||
webAppTemplatesDirInTemplatesDir = [reldir|react-app|]
|
||||
|
||||
-- | Path to the web app templates src/ directory, relative to the web app templates directory.
|
||||
srcDirInWebAppTemplatesDir :: Path' (Rel WebAppTemplatesDir) (Dir WebAppTemplatesSrcDir)
|
||||
srcDirInWebAppTemplatesDir = [reldir|src|]
|
||||
|
||||
mkSrcTmplFd :: Path' (Rel WebAppTemplatesSrcDir) File' -> FileDraft
|
||||
mkSrcTmplFd pathInTemplatesSrcDir = mkTmplFdWithDst srcPath dstPath
|
||||
where
|
||||
srcPath = srcDirInWebAppTemplatesDir </> pathInTemplatesSrcDir
|
||||
dstPath =
|
||||
webAppSrcDirInWebAppRootDir
|
||||
</> (SP.castRel pathInTemplatesSrcDir :: Path' (Rel WebAppSrcDir) File')
|
||||
|
||||
mkTmplFd :: Path' (Rel WebAppTemplatesDir) File' -> FileDraft
|
||||
mkTmplFd path = mkTmplFdWithDst path (SP.castRel path)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
|
||||
module Wasp.Generator.WebAppGenerator.RouterGenerator
|
||||
( generateRouter,
|
||||
( genRouter,
|
||||
)
|
||||
where
|
||||
|
||||
@ -61,8 +61,8 @@ instance ToJSON PageTemplateData where
|
||||
"importFrom" .= _importFrom pageTD
|
||||
]
|
||||
|
||||
generateRouter :: AppSpec -> Generator FileDraft
|
||||
generateRouter spec = do
|
||||
genRouter :: AppSpec -> Generator FileDraft
|
||||
genRouter spec = do
|
||||
return $
|
||||
C.mkTmplFdWithDstAndData
|
||||
(asTmplFile $ [reldir|src|] </> routerPath)
|
||||
|
@ -15,6 +15,7 @@ import qualified Wasp.Analyzer.TypeChecker as TC
|
||||
import qualified Wasp.AppSpec.Action as Action
|
||||
import qualified Wasp.AppSpec.App as App
|
||||
import qualified Wasp.AppSpec.App.Auth as Auth
|
||||
import qualified Wasp.AppSpec.App.Client as Client
|
||||
import qualified Wasp.AppSpec.App.Db as Db
|
||||
import qualified Wasp.AppSpec.App.Dependency as Dependency
|
||||
import qualified Wasp.AppSpec.App.Server as Server
|
||||
@ -50,6 +51,9 @@ spec_Analyzer = do
|
||||
" server: {",
|
||||
" setupFn: import { setupServer } from \"@ext/bar.js\"",
|
||||
" },",
|
||||
" client: {",
|
||||
" setupFn: import { setupClient } from \"@ext/baz.js\"",
|
||||
" },",
|
||||
" db: {",
|
||||
" system: PostgreSQL",
|
||||
" }",
|
||||
@ -127,6 +131,13 @@ spec_Analyzer = do
|
||||
(ExtImportField "setupServer")
|
||||
(fromJust $ SP.parseRelFileP "bar.js")
|
||||
},
|
||||
App.client =
|
||||
Just
|
||||
Client.Client
|
||||
{ Client.setupFn =
|
||||
Just $
|
||||
ExtImport (ExtImportField "setupClient") (fromJust $ SP.parseRelFileP "baz.js")
|
||||
},
|
||||
App.db = Just Db.Db {Db.system = Just Db.PostgreSQL}
|
||||
}
|
||||
)
|
||||
|
@ -160,6 +160,7 @@ library
|
||||
Wasp.AppSpec.Action
|
||||
Wasp.AppSpec.App
|
||||
Wasp.AppSpec.App.Auth
|
||||
Wasp.AppSpec.App.Client
|
||||
Wasp.AppSpec.App.Db
|
||||
Wasp.AppSpec.App.Dependency
|
||||
Wasp.AppSpec.App.Server
|
||||
|
@ -28,13 +28,17 @@ Head of your HTML Document. Your app's metadata (styles, links, etc) can be adde
|
||||
Authentication and authorization configuration.
|
||||
Check [`app.auth`](/docs/language/features#authentication--authorization) for more details.
|
||||
|
||||
#### `db: dict` (optional)
|
||||
Database configuration.
|
||||
Check [`app.db`](/docs/language/features#database) for more details.
|
||||
#### `client: dict` (optional)
|
||||
Client configuration.
|
||||
Check [`app.client`](/docs/language/features#client-configuration) for more details.
|
||||
|
||||
#### `server: dict` (optional)
|
||||
Server configuration.
|
||||
Check [`app.server`](/docs/language/features#server) for more details.
|
||||
Check [`app.server`](/docs/language/features#server-configuration) for more details.
|
||||
|
||||
#### `db: dict` (optional)
|
||||
Database configuration.
|
||||
Check [`app.db`](/docs/language/features#database-configuration) for more details.
|
||||
|
||||
#### `dependencies: [(string, string)]` (optional)
|
||||
List of dependencies (external libraries).
|
||||
@ -917,6 +921,45 @@ import AuthError from '@wasp/core/AuthError.js'
|
||||
}
|
||||
```
|
||||
|
||||
## Client configuration
|
||||
|
||||
You can configure the client using the `client` field inside the `app`
|
||||
declaration,
|
||||
|
||||
```c
|
||||
app MyApp {
|
||||
title: "My app",
|
||||
// ...
|
||||
client: {
|
||||
setupFn: import mySetupFunction from "@ext/myClientSetupCode.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`app.client` is a dictionary with the following fields:
|
||||
|
||||
#### `setupFn: ExtImport` (optional)
|
||||
|
||||
`setupFn` declares a JavaScript function that Wasp executes on the client
|
||||
before everything else. It is expected to be asynchronous, and
|
||||
Wasp will await its completion before rendering the page. The function takes no
|
||||
arguments, and its return value is ignored.
|
||||
|
||||
You can use this function to perform any custom setup (e.g., setting up
|
||||
client-side periodic jobs).
|
||||
|
||||
Here's a dummy example of such a function:
|
||||
|
||||
```js title="ext/myClientSetupCode.js"
|
||||
async function mySetupFunction() {
|
||||
let count = 1;
|
||||
setInterval(
|
||||
() => console.log(`You have been online for ${count++} hours.`),
|
||||
1000 * 60 * 60,
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Server configuration
|
||||
|
||||
Via `server` field of `app` declaration, you can configure behaviour of the Node.js server (one that is executing wasp operations).
|
||||
@ -995,7 +1038,7 @@ Any env vars defined in the `.env` will be forwarded to the server-side of your
|
||||
console.log(process.env.DATABASE_URL)
|
||||
```
|
||||
|
||||
## Database
|
||||
## Database configuration
|
||||
|
||||
Via `db` field of `app` declaration, you can configure the database used by Wasp.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user