Adds Wasp and Prisma syntax highlighting (#1209)

This commit is contained in:
Mihovil Ilakovac 2023-05-23 14:29:54 +02:00 committed by GitHub
parent cb26d71947
commit 9b867901aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 472 additions and 257 deletions

View File

@ -36,7 +36,7 @@ Here is what it looks like live: https://waspleau.netlify.app/ There is also a s
So, what do we need to get started? First, we need a way to schedule and run jobs; for this, we decided to use [Bull](https://github.com/OptimalBits/bull). Ok, lets wire it up. This should be easy, right? We can add external NPM dependencies in our Wasp files like so: So, what do we need to get started? First, we need a way to schedule and run jobs; for this, we decided to use [Bull](https://github.com/OptimalBits/bull). Ok, lets wire it up. This should be easy, right? We can add external NPM dependencies in our Wasp files like so:
```css title="main.wasp" ```wasp title="main.wasp"
app waspleau { app waspleau {
title: "Waspleau", title: "Waspleau",
@ -55,7 +55,7 @@ But where do we declare our queue and processing callback functions in Wasp? Uh
Thankfully, Waspleau can leverage a powerful and flexible [hook supplied by Wasp](https://wasp-lang.dev/docs/language/basic-elements#setupfn) called `server.setupFn`. This declares a JavaScript function that will be executed on server start. Yahoo! This means we can do things like the following: Thankfully, Waspleau can leverage a powerful and flexible [hook supplied by Wasp](https://wasp-lang.dev/docs/language/basic-elements#setupfn) called `server.setupFn`. This declares a JavaScript function that will be executed on server start. Yahoo! This means we can do things like the following:
```css title="main.wasp" ```wasp title="main.wasp"
app waspleau { app waspleau {
... ...
@ -130,7 +130,7 @@ _Note: Please see the [actual serverSetup.js file](https://github.com/wasp-lang/
We now have jobs running and data updating at regular intervals, nice, but we still need a way to send that data down the wire. Here, we expose the in-memory data from our `server.setupFn` module so our queries can also use it: We now have jobs running and data updating at regular intervals, nice, but we still need a way to send that data down the wire. Here, we expose the in-memory data from our `server.setupFn` module so our queries can also use it:
```css title="main.wasp" ```wasp title="main.wasp"
... ...
query dashboard { query dashboard {

View File

@ -90,7 +90,7 @@ However, we will also continue to expand the number of job execution runtimes we
If you are a regular reader of this blog (thank you, you deserve a raise! 😊), you may recall we created an example app of a metrics dashboard called [Waspleau](https://wasp-lang.dev/blog/2022/01/27/waspleau) that used workers in the background to make periodic HTTP calls for data. In that example, we didnt yet have access to recurring jobs in Wasp, so we used Bull for scheduled jobs instead. To set up our queue-related logic we had to have this huge `setupFn` wiring it all up; but now, we can remove all that code and simply use jobs instead! Here is what the new DSL looks like: If you are a regular reader of this blog (thank you, you deserve a raise! 😊), you may recall we created an example app of a metrics dashboard called [Waspleau](https://wasp-lang.dev/blog/2022/01/27/waspleau) that used workers in the background to make periodic HTTP calls for data. In that example, we didnt yet have access to recurring jobs in Wasp, so we used Bull for scheduled jobs instead. To set up our queue-related logic we had to have this huge `setupFn` wiring it all up; but now, we can remove all that code and simply use jobs instead! Here is what the new DSL looks like:
```js title=main.wasp ```wasp title=main.wasp
// A cron job for fetching GitHub stats // A cron job for fetching GitHub stats
job getGithubStats { job getGithubStats {
executor: PgBoss, executor: PgBoss,

View File

@ -172,7 +172,7 @@ Another thing to keep in mind is that we should also track how things change ove
If we try to apply the principles from above (less code, less detailed instructions, stating **what** we want instead of **how** it needs to be done), the code for auth might look something like this: If we try to apply the principles from above (less code, less detailed instructions, stating **what** we want instead of **how** it needs to be done), the code for auth might look something like this:
```css ```wasp
auth: { auth: {
userEntity: User, userEntity: User,
externalAuthEntity: SocialLogin, externalAuthEntity: SocialLogin,

View File

@ -62,7 +62,7 @@ psl=}
### Step 2 - Update `app.auth` to use these items ### Step 2 - Update `app.auth` to use these items
```css title="./main.wasp" ```wasp title="./main.wasp"
app authExample { app authExample {
// ... // ...
auth: { auth: {

View File

@ -64,7 +64,7 @@ We already decided that we would pick an ORM(ish) solution for JS/TS which we wo
But then we looked at Prisma, and the winner was clear! Not only was Prisma taking care of everything that we cared about, but it had one additional feature that made it a perfect fit: But then we looked at Prisma, and the winner was clear! Not only was Prisma taking care of everything that we cared about, but it had one additional feature that made it a perfect fit:
``` ```prisma
model User { model User {
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
username String @unique username String @unique
@ -82,8 +82,8 @@ So instead of implementing our own syntax for describing Entities, we decided to
Today, Entities are described like this in Wasp language: Today, Entities are described like this in Wasp language:
``` ```wasp
... some Wasp code ... // ... some Wasp code ...
entity User {=psl entity User {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
@ -91,7 +91,7 @@ entity User {=psl
password String password String
psl=} psl=}
... some Wasp code ... // ... some Wasp code ...
``` ```
So in the middle of Wasp, you just switch to writing PSL (Prisma Schema Language) to describe an entity! So in the middle of Wasp, you just switch to writing PSL (Prisma Schema Language) to describe an entity!

View File

@ -48,7 +48,7 @@ To honor the tradition of demonstrating UIs using Todo apps, We'll show you how
Before looking at our todo app in action, let's see how we've implemented it in Wasp. Before looking at our todo app in action, let's see how we've implemented it in Wasp.
These are the relevant declarations in our `.wasp` file: These are the relevant declarations in our `.wasp` file:
```javascript title=main.wasp ```wasp title=main.wasp
entity Task {=psl entity Task {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
description String description String

View File

@ -33,9 +33,7 @@ What makes Wasp unique is that its a framework that uses a super simple **lan
Check it out for yourself: Check it out for yourself:
```c ```wasp title="main.wasp"
/* main.wasp */
app todoApp { app todoApp {
title: "ToDo App",/* visible in tab */ title: "ToDo App",/* visible in tab */

View File

@ -173,7 +173,7 @@ Let's begin by creating the data models. Wasp uses Prisma under the hood to comm
I had to first declare all of the entities I needed with Prisma PSL in the Wasp config file. I had to first declare all of the entities I needed with Prisma PSL in the Wasp config file.
```jsx ```wasp
entity Phrase {=psl entity Phrase {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
group String group String
@ -228,7 +228,7 @@ Lets now look at what I needed to do to get the data flowing from the backend
First, I declared a query in my Wasp config file: First, I declared a query in my Wasp config file:
```c ```wasp
app phraseTutor { app phraseTutor {
... ...
} }
@ -265,7 +265,7 @@ Lets also add support for Google auth for our app. It involves declaring you
We declare it to the Wasp file by adding the `google` key under `auth`: We declare it to the Wasp file by adding the `google` key under `auth`:
```jsx ```wasp
app phraseTutor { app phraseTutor {
... ...
auth: { auth: {

View File

@ -14,7 +14,7 @@ Based on your `main.wasp` file on the authentication providers you enabled, the
For example, if you only enabled e-mail authentication: For example, if you only enabled e-mail authentication:
```c title="main.wasp" ```wasp title="main.wasp"
app MyApp { app MyApp {
title: "My app", title: "My app",
//... //...
@ -33,7 +33,7 @@ We'll get this:
And then we enable Google and Github: And then we enable Google and Github:
```c title="main.wasp" {7-8} ```wasp title="main.wasp" {7-8}
app MyApp { app MyApp {
title: "My app", title: "My app",
//... //...
@ -60,7 +60,7 @@ Let's take a look at the components that are available for you to use.
### Login form ### Login form
Useful for <span style={{ background: '#fee', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>username & password</span> and <span style={{ background: '#eef', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>email</span> authentication. Useful for <span className="pill pill-username-password">username & password</span> and <span className="pill pill-email">email</span> authentication.
![Login form](/img/authui/login.png) ![Login form](/img/authui/login.png)
@ -75,7 +75,7 @@ import { LoginForm } from '@wasp/auth/forms/Login'
### Signup form ### Signup form
Useful for <span style={{ background: '#fee', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>username & password</span> and <span style={{ background: '#eef', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>email</span> authentication. Useful for <span className="pill pill-username-password">username & password</span> and <span className="pill pill-email">email</span> authentication.
![Signup form](/img/authui/signup.png) ![Signup form](/img/authui/signup.png)
@ -90,7 +90,7 @@ import { SignupForm } from '@wasp/auth/forms/Signup'
### Forgot password form ### Forgot password form
Useful for <span style={{ background: '#eef', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>email</span> authentication. Useful for <span className="pill pill-email">email</span> authentication.
![Forgot password form](/img/authui/forgot_password.png) ![Forgot password form](/img/authui/forgot_password.png)
@ -105,7 +105,7 @@ import { ForgotPasswordForm } from '@wasp/auth/forms/ForgotPassword'
### Reset password form ### Reset password form
Useful for <span style={{ background: '#eef', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>email</span> authentication. Useful for <span className="pill pill-email">email</span> authentication.
![Reset password form](/img/authui/reset_password.png) ![Reset password form](/img/authui/reset_password.png)
@ -120,7 +120,7 @@ import { ResetPasswordForm } from '@wasp/auth/forms/ResetPassword'
### Verify email form ### Verify email form
Useful for <span style={{ background: '#eef', padding: '0.2rem 0.5rem', borderRadius: '0.375rem' }}>email</span> authentication. Useful for <span className="pill pill-email">email</span> authentication.
![Verify email form](/img/authui/email_verification.png) ![Verify email form](/img/authui/email_verification.png)

View File

@ -23,7 +23,7 @@ We'll need to take the following steps to set up email authentication:
Outline of the Wasp file we'll be working with: Outline of the Wasp file we'll be working with:
```c title="main.wasp" ```wasp title="main.wasp"
// Configuring e-mail authentication // Configuring e-mail authentication
app myApp { ... } app myApp { ... }
@ -40,7 +40,7 @@ page SignupPage { ... }
Let's first set up the email authentication by adding the following to our `main.wasp` file: Let's first set up the email authentication by adding the following to our `main.wasp` file:
```c title="main.wasp" ```wasp title="main.wasp"
app myApp { app myApp {
wasp: { wasp: {
version: "^0.10.0" version: "^0.10.0"
@ -79,7 +79,7 @@ app myApp {
Then we'll define the `User` entity in our `main.wasp` file: Then we'll define the `User` entity in our `main.wasp` file:
```c title="main.wasp" {4-8} ```wasp title="main.wasp" {4-8}
// 5. Define the user entity // 5. Define the user entity
entity User {=psl entity User {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
@ -99,7 +99,7 @@ Next, we need to define the routes and pages for the authentication pages. We'll
We'll add the following to our `main.wasp` file: We'll add the following to our `main.wasp` file:
```c title="main.wasp" ```wasp title="main.wasp"
// 6. Define the routes // 6. Define the routes
route SignupRoute { path: "/signup", to: SignupPage } route SignupRoute { path: "/signup", to: SignupPage }
page SignupPage { page SignupPage {
@ -133,7 +133,7 @@ We'll use SendGrid in this guide to send our e-mails. You can use any of the sup
To set up SendGrid to send emails, we will add the following to our `main.wasp` file: To set up SendGrid to send emails, we will add the following to our `main.wasp` file:
```c title="main.wasp" ```wasp title="main.wasp"
app myApp { app myApp {
... ...
emailSender: { emailSender: {
@ -224,7 +224,7 @@ By default, Wasp requires the e-mail to be verified before allowing the user to
Our setup looks like this: Our setup looks like this:
```c title="main.wasp" ```wasp title="main.wasp"
emailVerification: { emailVerification: {
clientRoute: EmailVerificationRoute, clientRoute: EmailVerificationRoute,
getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js", getEmailContentFn: import { getVerificationEmailContent } from "@server/auth/email.js",
@ -233,7 +233,7 @@ emailVerification: {
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `EmailVerificationRoute` route we defined in the `main.wasp` file. When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `EmailVerificationRoute` route we defined in the `main.wasp` file.
```c title="main.wasp" ```wasp title="main.wasp"
route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage } route EmailVerificationRoute { path: "/email-verification", to: EmailVerificationPage }
page EmailVerificationPage { page EmailVerificationPage {
component: import { EmailVerification } from "@client/pages/auth/EmailVerification.tsx", component: import { EmailVerification } from "@client/pages/auth/EmailVerification.tsx",
@ -294,7 +294,7 @@ Users can request a password and then they'll receive an e-mail with a link to r
Our setup in `main.wasp` looks like this: Our setup in `main.wasp` looks like this:
```c title="main.wasp" ```wasp title="main.wasp"
passwordReset: { passwordReset: {
clientRoute: PasswordResetRoute, clientRoute: PasswordResetRoute,
getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js", getEmailContentFn: import { getPasswordResetEmailContent } from "@server/auth/email.js",
@ -350,7 +350,7 @@ export const getPasswordResetEmailContent: GetPasswordResetEmailContentFn = ({
When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `PasswordResetRoute` route we defined in the `main.wasp` file. When the user receives an e-mail, they receive a link that goes to the client route specified in the `clientRoute` field. In our case, this is the `PasswordResetRoute` route we defined in the `main.wasp` file.
```c title="main.wasp" ```wasp title="main.wasp"
route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage } route PasswordResetRoute { path: "/password-reset", to: PasswordResetPage }
page PasswordResetPage { page PasswordResetPage {
component: import { PasswordReset } from "@client/pages/auth/PasswordReset.tsx", component: import { PasswordReset } from "@client/pages/auth/PasswordReset.tsx",

View File

@ -48,7 +48,7 @@ const defaultGlobalMiddleware: MiddlewareConfig = new Map([
If you would like to modify the middleware for _all_ operations and APIs, you can do something like: If you would like to modify the middleware for _all_ operations and APIs, you can do something like:
```c title=todoApp.wasp ```wasp title=todoApp.wasp
app todoApp { app todoApp {
// ... // ...
@ -75,7 +75,7 @@ export const serverMiddlewareFn: MiddlewareConfigFn = (middlewareConfig) => {
If you would like to modify the middleware for a single API, you can do something like: If you would like to modify the middleware for a single API, you can do something like:
```c title=todoApp.wasp ```wasp title=todoApp.wasp
api webhookCallback { api webhookCallback {
fn: import { webhookCallback } from "@server/apis.js", fn: import { webhookCallback } from "@server/apis.js",
middlewareConfigFn: import { webhookCallbackMiddlewareFn } from "@server/apis.js", middlewareConfigFn: import { webhookCallbackMiddlewareFn } from "@server/apis.js",
@ -116,7 +116,7 @@ router.post('/webhook/callback', webhookCallbackMiddleware, ...)
If you would like to modify the middleware for all API routes under some common path, you can do something like: If you would like to modify the middleware for all API routes under some common path, you can do something like:
```c title=todoApp.wasp ```wasp title=todoApp.wasp
apiNamespace fooBar { apiNamespace fooBar {
middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js", middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
path: "/foo/bar" path: "/foo/bar"

View File

@ -8,7 +8,7 @@ import SendingEmailsInDevelopment from '../_sendingEmailsInDevelopment.md'
With Wasp's email-sending feature, you can easily integrate email functionality into your web application. With Wasp's email-sending feature, you can easily integrate email functionality into your web application.
```js title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
... ...
emailSender: { emailSender: {
@ -73,7 +73,7 @@ For each provider, you'll need to set up env variables in the `.env.server` file
First, set the provider to `SMTP` in your `main.wasp` file. First, set the provider to `SMTP` in your `main.wasp` file.
```js title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
... ...
emailSender: { emailSender: {
@ -97,7 +97,7 @@ Many transactional email providers (e.g. Mailgun, SendGrid but also others) can
Set the provider to `Mailgun` in the `main.wasp` file. Set the provider to `Mailgun` in the `main.wasp` file.
```js title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
... ...
emailSender: { emailSender: {
@ -125,7 +125,7 @@ MAILGUN_DOMAIN=
Set the provider field to `SendGrid` in your `main.wasp` file. Set the provider field to `SendGrid` in your `main.wasp` file.
```js title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
... ...
emailSender: { emailSender: {

View File

@ -7,7 +7,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
# GitHub # GitHub
To implement GitHub Auth, you'll need to add the Auth object with the following configuration to your `main.wasp` file: To implement GitHub Auth, you'll need to add the Auth object with the following configuration to your `main.wasp` file:
```c title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
wasp: { wasp: {
version: "^0.8.0" version: "^0.8.0"

View File

@ -7,7 +7,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
# Google # Google
To implement Google Auth, you'll need to add the Auth object with the following configuration to your `main.wasp` file: To implement Google Auth, you'll need to add the Auth object with the following configuration to your `main.wasp` file:
```c title="main.wasp" ```wasp title="main.wasp"
app Example { app Example {
wasp: { wasp: {
version: "^0.8.0" version: "^0.8.0"

View File

@ -53,7 +53,7 @@ Let's say you want to build a web app that allows users to **create and share th
Let's start with the main.wasp file: it is the central file of your app, where you describe the app from the high level. Let's start with the main.wasp file: it is the central file of your app, where you describe the app from the high level.
Let's give our app a title and let's immediatelly turn on the full-stack authentication via username and password: Let's give our app a title and let's immediatelly turn on the full-stack authentication via username and password:
```c title="main.wasp" ```wasp title="main.wasp"
app RecipeApp { app RecipeApp {
title: "My Recipes", title: "My Recipes",
wasp: { version: "^0.10.0" }, wasp: { version: "^0.10.0" },
@ -67,7 +67,7 @@ app RecipeApp {
Let's then add the data models for your recipes. We will want to have Users and Users can own Recipes: Let's then add the data models for your recipes. We will want to have Users and Users can own Recipes:
```c title="main.wasp" ```wasp title="main.wasp"
... ...
entity User {=psl // Data models are defined using Prisma Schema Language. entity User {=psl // Data models are defined using Prisma Schema Language.
@ -92,7 +92,7 @@ We do that by defining Operations, in this case a Query `getRecipes` and Action
which are in their essence a Node.js functions that execute on server and can, thanks to Wasp, very easily be called from the client. which are in their essence a Node.js functions that execute on server and can, thanks to Wasp, very easily be called from the client.
First, we define these Operations in our main.wasp file, so Wasp knows about them and can "beef them up": First, we define these Operations in our main.wasp file, so Wasp knows about them and can "beef them up":
```c title="main.wasp" ```wasp title="main.wasp"
// Queries have automatic cache invalidation and are type-safe. // Queries have automatic cache invalidation and are type-safe.
query getRecipes { query getRecipes {
fn: import { getRecipes } from "@server/recipe.js", fn: import { getRecipes } from "@server/recipe.js",
@ -127,11 +127,11 @@ Now we can very easily use these in our React components!
For the end, let's create a home page of our app. For the end, let's create a home page of our app.
First we define it in main.wasp: First we define it in main.wasp:
```c title="main.wasp" ```wasp title="main.wasp"
... ...
route HomeRoute { path: "/", to: HomePage } route HomeRoute { path: "/", to: HomePage }
component HomePage { page HomePage {
component: import { HomePage } from "@client/pages/HomePage", component: import { HomePage } from "@client/pages/HomePage",
authRequired: true // Will send user to /login if not authenticated. authRequired: true // Will send user to /login if not authenticated.
} }

View File

@ -11,7 +11,7 @@ import SendingEmailsInDevelopment from '../_sendingEmailsInDevelopment.md'
There can be only one declaration of `app` type per Wasp project. There can be only one declaration of `app` type per Wasp project.
It serves as a starting point and defines global properties of your app. It serves as a starting point and defines global properties of your app.
```c ```wasp
app todoApp { app todoApp {
wasp: { wasp: {
version: "^0.6.0" version: "^0.6.0"
@ -67,7 +67,7 @@ Check [`app.emailSender`](/docs/language/features#email-sender) for more details
`page` declaration is the top-level layout abstraction. Your app can have multiple pages. `page` declaration is the top-level layout abstraction. Your app can have multiple pages.
```c ```wasp
page MainPage { page MainPage {
component: import Main from "@client/pages/Main", component: import Main from "@client/pages/Main",
authRequired: false // optional authRequired: false // optional
@ -94,7 +94,7 @@ Check out this [section of our Todo app tutorial](/docs/tutorials/todo-app/06-au
`route` declaration provides top-level routing functionality in Wasp. `route` declaration provides top-level routing functionality in Wasp.
```css ```wasp
route AboutRoute { path: "/about", to: AboutPage } route AboutRoute { path: "/about", to: AboutPage }
``` ```
@ -109,7 +109,7 @@ Name of the `page` to which the path will lead.
Referenced page must be defined somewhere in `.wasp` file. Referenced page must be defined somewhere in `.wasp` file.
### Example - parametrised URL path ### Example - parametrised URL path
```css ```wasp
route TaskRoute { path: "/task/:id", to: TaskPage } route TaskRoute { path: "/task/:id", to: TaskPage }
``` ```
For details on URL path format check [React Router](https://reactrouter.com/web/) For details on URL path format check [React Router](https://reactrouter.com/web/)
@ -121,7 +121,7 @@ Since Wasp under the hood generates code with [React Router](https://reactrouter
the same rules apply when accessing URL params in your React components. Here is an example just to get you the same rules apply when accessing URL params in your React components. Here is an example just to get you
started: started:
```c title="todoApp.wasp" ```wasp title="todoApp.wasp"
// ... // ...
route TaskRoute { path: "/task/:id", to: TaskPage } route TaskRoute { path: "/task/:id", to: TaskPage }
page TaskPage { page TaskPage {
@ -147,7 +147,7 @@ export default Task
Navigation can be performed from the React code via `<Link/>` component, also using the functionality of Navigation can be performed from the React code via `<Link/>` component, also using the functionality of
[React Router](https://reactrouter.com/web/): [React Router](https://reactrouter.com/web/):
```c title="todoApp.wasp" ```wasp title="todoApp.wasp"
// ... // ...
route HomeRoute { path: "/home", to: HomePage } route HomeRoute { path: "/home", to: HomePage }
page HomePage { page HomePage {
@ -173,7 +173,7 @@ Wasp uses [Prisma](https://www.prisma.io/) to implement database functionality a
Each `Entity` declaration corresponds 1-to-1 to Prisma data model and is defined in a following way: Each `Entity` declaration corresponds 1-to-1 to Prisma data model and is defined in a following way:
```css ```wasp
entity Task {=psl entity Task {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
description String description String
@ -270,7 +270,7 @@ We'll leave this option aside for now. You can read more about it [here](#using-
Wasp Queries and their implementations don't need to (but can) have the same name, so we will keep the names different to avoid confusion. Wasp Queries and their implementations don't need to (but can) have the same name, so we will keep the names different to avoid confusion.
With that in mind, this is how you might declare the Queries that use the implementations from the previous step: With that in mind, this is how you might declare the Queries that use the implementations from the previous step:
```c title="pages/main.wasp" ```wasp title="pages/main.wasp"
// ... // ...
// Again, it most likely makes sense to name the Wasp Query after // Again, it most likely makes sense to name the Wasp Query after
@ -390,7 +390,7 @@ To prevent information leakage, the server won't forward these fields for any ot
In most cases, resources used in Queries will be [Entities](#entity). In most cases, resources used in Queries will be [Entities](#entity).
To use an Entity in your Query, add it to the query declaration in Wasp: To use an Entity in your Query, add it to the query declaration in Wasp:
```c {4,9} title="main.wasp" ```wasp {4,9} title="main.wasp"
query fetchAllTasks { query fetchAllTasks {
fn: import { getAllTasks } from "@server/queries.js", fn: import { getAllTasks } from "@server/queries.js",
@ -436,7 +436,7 @@ export const sayHi = async () => {
``` ```
Its corresponding declaration in Wasp: Its corresponding declaration in Wasp:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
action sayHi { action sayHi {
@ -463,7 +463,7 @@ export const updateTaskIsDone = ({ id, isDone }, context) => {
}) })
} }
``` ```
```c title=main.wasp ```wasp title=main.wasp
action updateTaskIsDone { action updateTaskIsDone {
fn: import { updateTaskIsDone } from "@server/actions.js", fn: import { updateTaskIsDone } from "@server/actions.js",
entities: [Task] entities: [Task]
@ -471,7 +471,7 @@ action updateTaskIsDone {
``` ```
And here is how you might use it: And here is how you might use it:
```js {4,18} title=src/client/pages/Task.js ```jsx {4,18} title=src/client/pages/Task.js
import React from 'react' import React from 'react'
import { useQuery } from '@wasp/queries' import { useQuery } from '@wasp/queries'
import fetchTask from '@wasp/queries/fetchTask' import fetchTask from '@wasp/queries/fetchTask'
@ -639,7 +639,7 @@ export const fooBar : FooBar = (req, res, context) => {
##### More complicated TypeScript example ##### More complicated TypeScript example
Let's say you wanted to create some `GET` route that would take an email address as a param, and provide them the answer to "Life, the Universe and Everything." :) What would this look like in TypeScript? Let's say you wanted to create some `GET` route that would take an email address as a param, and provide them the answer to "Life, the Universe and Everything." :) What would this look like in TypeScript?
```c title="main.wasp" ```wasp title="main.wasp"
api fooBar { api fooBar {
fn: import { fooBar } from "@server/apis.js", fn: import { fooBar } from "@server/apis.js",
entities: [Task], entities: [Task],
@ -672,7 +672,7 @@ We'll leave this option aside for now. You can read more about it [here](#using-
- `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/guides/middleware-customization#2-customize-api-specific-middleware). - `middlewareConfigFn: ServerImport` (optional) - The import statement to an Express middleware config function for this API. See [the guide here](/docs/guides/middleware-customization#2-customize-api-specific-middleware).
Wasp APIs and their implementations don't need to (but can) have the same name. With that in mind, this is how you might declare the API that uses the implementations from the previous step: Wasp APIs and their implementations don't need to (but can) have the same name. With that in mind, this is how you might declare the API that uses the implementations from the previous step:
```c title="pages/main.wasp" ```wasp title="pages/main.wasp"
// ... // ...
api fooBar { api fooBar {
@ -711,7 +711,7 @@ export const Foo = () => {
In many cases, resources used in APIs will be [Entities](#entity). In many cases, resources used in APIs will be [Entities](#entity).
To use an Entity in your API, add it to the `api` declaration in Wasp: To use an Entity in your API, add it to the `api` declaration in Wasp:
```c {3} title="main.wasp" ```wasp {3} title="main.wasp"
api fooBar { api fooBar {
fn: import { fooBar } from "@server/apis.js", fn: import { fooBar } from "@server/apis.js",
entities: [Task], entities: [Task],
@ -735,7 +735,7 @@ The object `context.entities.Task` exposes `prisma.task` from [Prisma's CRUD API
An `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path. For example: An `apiNamespace` is a simple declaration used to apply some `middlewareConfigFn` to all APIs under some specific path. For example:
```c title="main.wasp" ```wasp title="main.wasp"
apiNamespace fooBar { apiNamespace fooBar {
middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js", middlewareConfigFn: import { fooBarNamespaceMiddlewareFn } from "@server/apis.js",
path: "/foo/bar" path: "/foo/bar"
@ -793,7 +793,7 @@ Keep in mind that pg-boss jobs run alongside your other server-side code, so the
To declare a `job` in Wasp, simply add a declaration with a reference to an `async` function, like the following: To declare a `job` in Wasp, simply add a declaration with a reference to an `async` function, like the following:
```c title="main.wasp" ```wasp title="main.wasp"
job mySpecialJob { job mySpecialJob {
executor: PgBoss, executor: PgBoss,
perform: { perform: {
@ -822,7 +822,7 @@ Note that in our example, `foo` takes an argument, but this does not always have
If you have work that needs to be done on some recurring basis, you can add a `schedule` to your job declaration: If you have work that needs to be done on some recurring basis, you can add a `schedule` to your job declaration:
```c {6-9} title="main.wasp" ```wasp {6-9} title="main.wasp"
job mySpecialJob { job mySpecialJob {
executor: PgBoss, executor: PgBoss,
perform: { perform: {
@ -840,7 +840,7 @@ In this example, you do _not_ need to invoke anything in JavaScript. You can ima
### Fully specified example ### Fully specified example
Both `perform` and `schedule` accept `executorOptions`, which we pass directly to the named job executor when you submit jobs. In this example, the scheduled job will have a `retryLimit` set to 0, as `schedule` overrides any similar property from `perform`. Lastly, we add an entity to pass in via the context argument to `perform.fn`. Both `perform` and `schedule` accept `executorOptions`, which we pass directly to the named job executor when you submit jobs. In this example, the scheduled job will have a `retryLimit` set to 0, as `schedule` overrides any similar property from `perform`. Lastly, we add an entity to pass in via the context argument to `perform.fn`.
```c ```wasp
job mySpecialJob { job mySpecialJob {
executor: PgBoss, executor: PgBoss,
perform: { perform: {
@ -951,7 +951,7 @@ There will also be namespaced, job executor-specific objects.
You can specify additional npm dependencies via `dependencies` field in `app` declaration, in following way: You can specify additional npm dependencies via `dependencies` field in `app` declaration, in following way:
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -974,7 +974,7 @@ In the future, we will add support for picking any version you like, but we have
Wasp provides authentication and authorization support out-of-the-box. Enabling it for your app is optional and can be done by configuring the `auth` field of the `app` declaration: Wasp provides authentication and authorization support out-of-the-box. Enabling it for your app is optional and can be done by configuring the `auth` field of the `app` declaration:
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
//... //...
@ -1026,7 +1026,7 @@ Automatic redirect on successful login only works when using the Wasp provided [
`usernameAndPassword` authentication method makes it possible to signup/login into the app by using a username and password. `usernameAndPassword` authentication method makes it possible to signup/login into the app by using a username and password.
This method requires that `userEntity` specified in `auth` contains `username: string` and `password: string` fields: This method requires that `userEntity` specified in `auth` contains `username: string` and `password: string` fields:
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
//... //...
@ -1213,7 +1213,7 @@ In the future, we will lift this limitation and enable smarter merging of accoun
`email` authentication method makes it possible to signup/login into the app by using an e-mail and a password. `email` authentication method makes it possible to signup/login into the app by using an e-mail and a password.
```c title="main.wasp" ```wasp title="main.wasp"
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -1251,7 +1251,7 @@ This method requires that `userEntity` specified in `auth` contains:
#### Fields in the `email` dict #### Fields in the `email` dict
```c title="main.wasp" ```wasp title="main.wasp"
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -1401,7 +1401,7 @@ When using Social Login Providers, Wasp gives you the following options:
<Tabs> <Tabs>
<TabItem value="google" label="Google" default> <TabItem value="google" label="Google" default>
```c ```wasp
auth: { auth: {
userEntity: User, userEntity: User,
externalAuthEntity: SocialLogin, externalAuthEntity: SocialLogin,
@ -1422,7 +1422,7 @@ When using Social Login Providers, Wasp gives you the following options:
</TabItem> </TabItem>
<TabItem value="gitHub" label="GitHub"> <TabItem value="gitHub" label="GitHub">
```c ```wasp
auth: { auth: {
userEntity: User, userEntity: User,
externalAuthEntity: SocialLogin, externalAuthEntity: SocialLogin,
@ -1452,7 +1452,7 @@ It is also posslbe to [override the default](features#overrides-for-social-login
#### `externalAuthEntity` #### `externalAuthEntity`
Anytime an authentication method is used that relies on an external authorization provider, for example, Google, we require an `externalAuthEntity` specified in `auth`, in addition to the `userEntity`, that contains the following configuration: Anytime an authentication method is used that relies on an external authorization provider, for example, Google, we require an `externalAuthEntity` specified in `auth`, in addition to the `userEntity`, that contains the following configuration:
```c {4,14} ```wasp {4,14}
//... //...
auth: { auth: {
userEntity: User, userEntity: User,
@ -1519,7 +1519,7 @@ Alternatively, you could add a `displayName` property to your User entity and as
We also show you how to customize the configuration of the Provider's settings using: We also show you how to customize the configuration of the Provider's settings using:
- the `configFn` function - the `configFn` function
```c title=main.wasp {9,10,13,14,26} ```wasp title=main.wasp {9,10,13,14,26}
app Example { app Example {
//... //...
@ -1726,7 +1726,7 @@ should be denied access to it.
You can configure the client using the `client` field inside the `app` You can configure the client using the `client` field inside the `app`
declaration, declaration,
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -1870,7 +1870,7 @@ explained in
Via `server` field of `app` declaration, you can configure behaviour of the Node.js server (one that is executing wasp operations). Via `server` field of `app` declaration, you can configure behaviour of the Node.js server (one that is executing wasp operations).
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -1978,7 +1978,7 @@ console.log(process.env.DATABASE_URL)
Via `db` field of `app` declaration, you can configure the database used by Wasp. Via `db` field of `app` declaration, you can configure the database used by Wasp.
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...
@ -2046,7 +2046,7 @@ Seeding is most commonly used for two following scenarios:
Wasp enables you to define multiple **seed functions** via `app.db.seeds`: Wasp enables you to define multiple **seed functions** via `app.db.seeds`:
```c ```wasp
app MyApp { app MyApp {
// ... // ...
db: { db: {
@ -2127,7 +2127,7 @@ Check out [our guide](/docs/guides/sending-emails#using-the-mailgun-provider) fo
You can optionally provide a default sender info that will be used when you don't provide it explicitly when sending an e-mail. You can optionally provide a default sender info that will be used when you don't provide it explicitly when sending an e-mail.
```c ```wasp
app MyApp { app MyApp {
title: "My app", title: "My app",
// ... // ...

View File

@ -42,7 +42,7 @@ TodoApp/
- shared/ - shared/
``` ```
```css title="main.wasp" ```wasp title="main.wasp"
app todoApp { app todoApp {
wasp: { wasp: {
version: "^0.6.0" version: "^0.6.0"

View File

@ -8,7 +8,7 @@ Wasp is a declarative, statically typed, domain specific language (DSL).
The central point of Wasp language are **declarations**, and Wasp source is at the end just a bunch of declarations, each of them describing a part of your web app. The central point of Wasp language are **declarations**, and Wasp source is at the end just a bunch of declarations, each of them describing a part of your web app.
```c ```wasp
app MyApp { app MyApp {
title: "My app" title: "My app"
} }

View File

@ -85,7 +85,7 @@ Let's start with the `main.wasp` file, which introduces 3 new concepts:
[page](language/features.md#page) and [page](language/features.md#page) and
[route](language/features.md#route). [route](language/features.md#route).
```c title="main.wasp" ```wasp title="main.wasp"
app TodoApp { // Main declaration, defines a new web app. app TodoApp { // Main declaration, defines a new web app.
wasp: { wasp: {
version: "^0.10.0" version: "^0.10.0"

View File

@ -8,7 +8,7 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
[Entities](language/features.md#entity) are one of the very central concepts in Wasp, and they mainly play the role of data models. [Entities](language/features.md#entity) are one of the very central concepts in Wasp, and they mainly play the role of data models.
Since our TodoApp is all about tasks, we will define a Task entity in Wasp: Since our TodoApp is all about tasks, we will define a Task entity in Wasp:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
entity Task {=psl entity Task {=psl

View File

@ -25,7 +25,7 @@ It consists of a declaration in Wasp and implementation in JS (in `src/server/`
### Wasp declaration ### Wasp declaration
Add the following code to `main.wasp`: Add the following code to `main.wasp`:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
query getTasks { query getTasks {

View File

@ -15,7 +15,7 @@ Creating an action is very similar to creating a query.
### Wasp declaration ### Wasp declaration
First, we declare the action in Wasp: First, we declare the action in Wasp:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
action createTask { action createTask {

View File

@ -16,7 +16,7 @@ For that, we will need to do two things:
### Wasp declaration ### Wasp declaration
We declare a Wasp action: We declare a Wasp action:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
action updateTask { action updateTask {

View File

@ -18,7 +18,7 @@ Let's define a Todo list (luckily we have an app for that now ;)) to get this do
## Adding entity User ## Adding entity User
First, let's define the `User` entity: First, let's define the `User` entity:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
entity User {=psl entity User {=psl
@ -37,7 +37,7 @@ to propagate the schema change (we added User).
## Defining `app.auth` ## Defining `app.auth`
Next, we want to tell Wasp that we want full-stack [authentication](language/features.md#authentication--authorization) in our app, and that it should use the `User` entity for it: Next, we want to tell Wasp that we want full-stack [authentication](language/features.md#authentication--authorization) in our app, and that it should use the `User` entity for it:
```c {7-16} title="main.wasp" ```wasp {7-16} title="main.wasp"
app TodoApp { app TodoApp {
wasp: { wasp: {
version: "^0.7.0" version: "^0.7.0"
@ -76,7 +76,7 @@ To recap, so far we have defined:
When we defined `app.auth` we got login and signup forms generated for us, but now we have to create Login and Signup pages that use them. In our `main.wasp` file we'll add the following: When we defined `app.auth` we got login and signup forms generated for us, but now we have to create Login and Signup pages that use them. In our `main.wasp` file we'll add the following:
```c title="main.wasp" ```wasp title="main.wasp"
// ... // ...
route SignupRoute { path: "/signup", to: SignupPage } route SignupRoute { path: "/signup", to: SignupPage }
@ -141,7 +141,7 @@ Now, let's see how we're going to handle the situation when the user is not logg
`MainPage` page is a private page and we want users to be able to see it only if they are authenticated. `MainPage` page is a private page and we want users to be able to see it only if they are authenticated.
Wasp allows you to simply enforce private pages using the `authRequired` field: Wasp allows you to simply enforce private pages using the `authRequired` field:
```c {3} title="main.wasp" ```wasp {3} title="main.wasp"
// ... // ...
page MainPage { page MainPage {
authRequired: true, authRequired: true,
@ -186,7 +186,7 @@ That is because we did not yet update queries and actions to work only on the cu
## Defining User-Task relation in entities ## Defining User-Task relation in entities
First, let's define a one-to-many relation between User and Task (check the [prisma docs on relations](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/relations)): First, let's define a one-to-many relation between User and Task (check the [prisma docs on relations](https://www.prisma.io/docs/reference/tools-and-interfaces/prisma-schema/relations)):
```c {6,13-14} title="main.wasp" ```wasp {6,13-14} title="main.wasp"
// ... // ...
entity User {=psl entity User {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())

View File

@ -10,7 +10,7 @@ What is a Todo app without some clocks!? Well, still a Todo app, but certainly n
So, let's add a couple of clocks to our app, to help us track time while we perform our tasks (and to demonstrate the `app.dependencies` feature). So, let's add a couple of clocks to our app, to help us track time while we perform our tasks (and to demonstrate the `app.dependencies` feature).
For this, we will use the `react-clock` library from NPM. We can add it to our project as a [dependency](language/features.md#dependencies) like this: For this, we will use the `react-clock` library from NPM. We can add it to our project as a [dependency](language/features.md#dependencies) like this:
```c {4-6} title="main.wasp" ```wasp {4-6} title="main.wasp"
app TodoApp { app TodoApp {
// ... // ...

View File

@ -35,7 +35,7 @@ Our scaffolding already includes TypeScript, so migrating your project to TypeSc
Let's first assume your Wasp file contains the following definitions: Let's first assume your Wasp file contains the following definitions:
```c title=main.wasp ```wasp title=main.wasp
entity Task {=psl entity Task {=psl
id Int @id @default(autoincrement()) id Int @id @default(autoincrement())
description String description String
@ -103,7 +103,7 @@ You don't need to change anything inside the `.wasp` file.
Even when you use TypeScript, and your file is called `queries.ts`, you still need to import it using the `.js` extension: Even when you use TypeScript, and your file is called `queries.ts`, you still need to import it using the `.js` extension:
```c ```wasp
query getTaskInfo { query getTaskInfo {
fn: import { getTaskInfo } from "@server/queries.js", fn: import { getTaskInfo } from "@server/queries.js",
entities: [Task] entities: [Task]
@ -166,7 +166,7 @@ The mentioned type safety mechanisms also apply here: changing the task entity i
Wasp automatically generates the appropriate types for all Operations (i.e., Actions and Queries) you define inside your `.wasp` file. Assuming your `.wasp` file contains the following definition: Wasp automatically generates the appropriate types for all Operations (i.e., Actions and Queries) you define inside your `.wasp` file. Assuming your `.wasp` file contains the following definition:
```c title=main.wasp ```wasp title=main.wasp
// ... // ...
query GetTaskInfo { query GetTaskInfo {

View File

@ -1,5 +1,4 @@
const lightCodeTheme = require("prism-react-renderer/themes/github"); const lightCodeTheme = require("prism-react-renderer/themes/github");
const darkCodeTheme = require("prism-react-renderer/themes/dracula");
/** @type {import('@docusaurus/types').DocusaurusConfig} */ /** @type {import('@docusaurus/types').DocusaurusConfig} */
module.exports = { module.exports = {
@ -82,6 +81,7 @@ module.exports = {
}, },
prism: { prism: {
additionalLanguages: ["shell-session", "haskell"], additionalLanguages: ["shell-session", "haskell"],
theme: lightCodeTheme,
}, },
footer: { footer: {
style: "dark", style: "dark",
@ -157,7 +157,7 @@ module.exports = {
// Please change this to your repo. // Please change this to your repo.
blogSidebarCount: "ALL", blogSidebarCount: "ALL",
blogSidebarTitle: "All our posts", blogSidebarTitle: "All our posts",
postsPerPage: 'ALL', postsPerPage: "ALL",
editUrl: "https://github.com/wasp-lang/wasp/edit/main/web/blog", editUrl: "https://github.com/wasp-lang/wasp/edit/main/web/blog",
}, },
theme: { theme: {

121
web/package-lock.json generated
View File

@ -20,11 +20,11 @@
"plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"prismjs": "^1.29.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-modal": "^3.14.3", "react-modal": "^3.14.3",
"react-syntax-highlighter": "^15.5.0",
"react-tooltip": "^4.5.1", "react-tooltip": "^4.5.1",
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"tailwindcss": "^3.2.4" "tailwindcss": "^3.2.4"
@ -5844,17 +5844,6 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"node_modules/fault": {
"version": "1.0.4",
"license": "MIT",
"dependencies": {
"format": "^0.2.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/faye-websocket": { "node_modules/faye-websocket": {
"version": "0.11.4", "version": "0.11.4",
"license": "Apache-2.0", "license": "Apache-2.0",
@ -6124,12 +6113,6 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/format": {
"version": "0.2.2",
"engines": {
"node": ">=0.4.x"
}
},
"node_modules/forwarded": { "node_modules/forwarded": {
"version": "0.2.0", "version": "0.2.0",
"license": "MIT", "license": "MIT",
@ -6554,13 +6537,6 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/highlight.js": {
"version": "10.7.3",
"license": "BSD-3-Clause",
"engines": {
"node": "*"
}
},
"node_modules/history": { "node_modules/history": {
"version": "4.10.1", "version": "4.10.1",
"license": "MIT", "license": "MIT",
@ -7453,18 +7429,6 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/lowlight": {
"version": "1.20.0",
"license": "MIT",
"dependencies": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/lru-cache": { "node_modules/lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"license": "ISC", "license": "ISC",
@ -8965,7 +8929,8 @@
}, },
"node_modules/prismjs": { "node_modules/prismjs": {
"version": "1.29.0", "version": "1.29.0",
"license": "MIT", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==",
"engines": { "engines": {
"node": ">=6" "node": ">=6"
} }
@ -9431,20 +9396,6 @@
"react": ">=15" "react": ">=15"
} }
}, },
"node_modules/react-syntax-highlighter": {
"version": "15.5.0",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.27.0",
"refractor": "^3.6.0"
},
"peerDependencies": {
"react": ">= 0.14.0"
}
},
"node_modules/react-textarea-autosize": { "node_modules/react-textarea-autosize": {
"version": "8.4.1", "version": "8.4.1",
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz",
@ -9543,26 +9494,6 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/refractor": {
"version": "3.6.0",
"license": "MIT",
"dependencies": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.27.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/refractor/node_modules/prismjs": {
"version": "1.27.0",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/regenerate": { "node_modules/regenerate": {
"version": "1.4.2", "version": "1.4.2",
"license": "MIT" "license": "MIT"
@ -16030,12 +15961,6 @@
"reusify": "^1.0.4" "reusify": "^1.0.4"
} }
}, },
"fault": {
"version": "1.0.4",
"requires": {
"format": "^0.2.0"
}
},
"faye-websocket": { "faye-websocket": {
"version": "0.11.4", "version": "0.11.4",
"requires": { "requires": {
@ -16203,9 +16128,6 @@
} }
} }
}, },
"format": {
"version": "0.2.2"
},
"forwarded": { "forwarded": {
"version": "0.2.0" "version": "0.2.0"
}, },
@ -16468,9 +16390,6 @@
"he": { "he": {
"version": "1.2.0" "version": "1.2.0"
}, },
"highlight.js": {
"version": "10.7.3"
},
"history": { "history": {
"version": "4.10.1", "version": "4.10.1",
"requires": { "requires": {
@ -16979,13 +16898,6 @@
"lowercase-keys": { "lowercase-keys": {
"version": "1.0.1" "version": "1.0.1"
}, },
"lowlight": {
"version": "1.20.0",
"requires": {
"fault": "^1.0.0",
"highlight.js": "~10.7.0"
}
},
"lru-cache": { "lru-cache": {
"version": "5.1.1", "version": "5.1.1",
"requires": { "requires": {
@ -17774,7 +17686,9 @@
"requires": {} "requires": {}
}, },
"prismjs": { "prismjs": {
"version": "1.29.0" "version": "1.29.0",
"resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz",
"integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q=="
}, },
"process-nextick-args": { "process-nextick-args": {
"version": "2.0.1" "version": "2.0.1"
@ -18074,16 +17988,6 @@
"tiny-warning": "^1.0.0" "tiny-warning": "^1.0.0"
} }
}, },
"react-syntax-highlighter": {
"version": "15.5.0",
"requires": {
"@babel/runtime": "^7.3.1",
"highlight.js": "^10.4.1",
"lowlight": "^1.17.0",
"prismjs": "^1.27.0",
"refractor": "^3.6.0"
}
},
"react-textarea-autosize": { "react-textarea-autosize": {
"version": "8.4.1", "version": "8.4.1",
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.4.1.tgz",
@ -18147,19 +18051,6 @@
"minimatch": "^3.0.5" "minimatch": "^3.0.5"
} }
}, },
"refractor": {
"version": "3.6.0",
"requires": {
"hastscript": "^6.0.0",
"parse-entities": "^2.0.0",
"prismjs": "~1.27.0"
},
"dependencies": {
"prismjs": {
"version": "1.27.0"
}
}
},
"regenerate": { "regenerate": {
"version": "1.4.2" "version": "1.4.2"
}, },

View File

@ -26,11 +26,11 @@
"plugin-image-zoom": "github:flexanalytics/plugin-image-zoom", "plugin-image-zoom": "github:flexanalytics/plugin-image-zoom",
"postcss": "^8.4.19", "postcss": "^8.4.19",
"prism-react-renderer": "^1.3.5", "prism-react-renderer": "^1.3.5",
"prismjs": "^1.29.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-modal": "^3.14.3", "react-modal": "^3.14.3",
"react-syntax-highlighter": "^15.5.0",
"react-tooltip": "^4.5.1", "react-tooltip": "^4.5.1",
"react-transition-group": "^4.4.5", "react-transition-group": "^4.4.5",
"tailwindcss": "^3.2.4" "tailwindcss": "^3.2.4"

View File

@ -0,0 +1,39 @@
import React, { useEffect } from "react";
import Prism from "prismjs";
import "../css/prismjs-github-theme.css";
export default function CodeHighlight(props = {}) {
const codeRef = React.createRef();
const {
prefixCls = "code-highlight-wrapper",
className,
language,
source,
children,
...others
} = props;
const langCls = language ? `language-${language}` : "";
async function highlight() {
if (codeRef.current) {
Prism.highlightElement(codeRef.current);
}
}
useEffect(() => {
highlight();
}, [language, source]);
return (
<pre
className={`${prefixCls} ${className || ""} ${langCls}`}
{...others}
style={{
borderBottomLeftRadius: "10px",
borderBottomRightRadius: "10px",
paddingLeft: "15px",
}}
>
<code className={langCls} ref={codeRef}>
{source || children}
</code>
</pre>
);
}

View File

@ -1,7 +1,10 @@
import React from 'react' import React from 'react'
import Link from '@docusaurus/Link' import Link from '@docusaurus/Link'
import SyntaxHighlighter from 'react-syntax-highlighter'
import { qtcreatorLight, atomOneLight, atomOneDark, a11ylight } from 'react-syntax-highlighter/dist/cjs/styles/hljs' import './prismCustomization'
import CodeHighlight from './CodeHighlight'
import { Terminal, ArrowUpRight, Play, BookOpen, Grid, Layout, Trello } from 'react-feather' import { Terminal, ArrowUpRight, Play, BookOpen, Grid, Layout, Trello } from 'react-feather'
// Terminal, BookOpen, Grid, Layout, Trello, FileText // Terminal, BookOpen, Grid, Layout, Trello, FileText
@ -174,20 +177,12 @@ entity Task {=psl ... psl=} // Your Prisma data model.
<div className='bg-yellow-500 h-2 w-2 rounded-full' /> <div className='bg-yellow-500 h-2 w-2 rounded-full' />
</div> </div>
</div> </div>
{/* Editor body */} {/* Editor body */}
<div className='w-full text-sm shadow-2xl rounded-b-md'> <div className='w-full text-sm shadow-2xl rounded-b-md'>
<SyntaxHighlighter <CodeHighlight
language="javascript" language='wasp'
style={atomOneLight} source={codeString}
customStyle={{ />
borderBottomLeftRadius: '10px',
borderBottomRightRadius: '10px',
paddingLeft: '15px',
}}
>
{codeString}
</SyntaxHighlighter>
</div> {/* EOF code block wrapper */} </div> {/* EOF code block wrapper */}
</div> {/* EOF wrapper of header + code */} </div> {/* EOF wrapper of header + code */}
</div> {/* EOF col-span-6 */} </div> {/* EOF col-span-6 */}

View File

@ -0,0 +1,6 @@
import Prism from "prismjs";
import addWaspLangauge from "../prism/wasp";
import addPrismaLanguage from "../prism/prisma";
addPrismaLanguage(Prism);
addWaspLangauge(Prism);

View File

@ -5,7 +5,7 @@
* work well for content-centric websites. * work well for content-centric websites.
*/ */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
@tailwind base; @tailwind base;
@tailwind components; @tailwind components;
@ -21,18 +21,37 @@
} }
} }
/* Docusaurus stuff */ /* Docusaurus stuff */
.blog-list-page { .blog-list-page {
background-color: var(--custom-blog-list-background-color); background-color: var(--custom-blog-list-background-color);
} }
.pill {
padding: 0.2rem 0.5rem;
border-radius: 0.375rem;
}
.pill.pill-username-password {
background-color: var(--auth-ui-username-password-bg);
}
.pill.pill-email {
background-color: var(--auth-ui-email-bg);
}
/* Highlight Prisma field types properly */
.token.type-class-name {
color: rgb(54, 172, 170);
}
.token.annotation {
color: rgb(116, 116, 116) !important;
}
/* You can override the default Infima variables here. */ /* You can override the default Infima variables here. */
:root { :root {
/* Our custom values */ /* Our custom values */
--custom-background-color: #FDFDFD; --custom-background-color: #fdfdfd;
--custom-background-color-diff: #f4f4f4; --custom-background-color-diff: #f4f4f4;
--custom-shadow-lw: 0 3px 5px 0px rgba(0, 0, 0, 0.1); --custom-shadow-lw: 0 3px 5px 0px rgba(0, 0, 0, 0.1);
--custom-border-radius: 3px; --custom-border-radius: 3px;
@ -52,8 +71,8 @@
/* Infima overrides */ /* Infima overrides */
--ifm-container-width-xl: 1280px; --ifm-container-width-xl: 1280px;
--ifm-font-family-base: 'Inter'; --ifm-font-family-base: "Inter";
--ifm-color-primary: #BF9900; /* wasp color (ffcc00) darkened by 25% */ --ifm-color-primary: #bf9900; /* wasp color (ffcc00) darkened by 25% */
--ifm-color-primary-dark: #8a6f04; --ifm-color-primary-dark: #8a6f04;
--ifm-color-primary-darker: rgb(31, 165, 136); --ifm-color-primary-darker: rgb(31, 165, 136);
--ifm-color-primary-darkest: rgb(26, 136, 112); --ifm-color-primary-darkest: rgb(26, 136, 112);
@ -76,14 +95,19 @@
--ifm-h2-font-size: 2rem; --ifm-h2-font-size: 2rem;
--ifm-col-spacing-vertical: 0.5rem; --ifm-col-spacing-vertical: 0.5rem;
--docusaurus-highlighted-code-line-bg: #e8edf2;
--auth-ui-username-password-bg: #fee;
--auth-ui-email-bg: #eef;
} }
:root[data-theme='dark'] { :root[data-theme="dark"] {
--custom-background-color-diff: #2A2A2A; --custom-background-color-diff: #2a2a2a;
/* Blog */ /* Blog */
--custom-blog-list-background-color: var(--ifm-background-color); --custom-blog-list-background-color: var(--ifm-background-color);
--custom-blog-card-timestamp-color: #a3a3a3; --custom-blog-card-timestamp-color: #a3a3a3;
--custom-blog-card-background-color: black; --custom-blog-card-background-color: black;
--docusaurus-highlighted-code-line-bg: #dee6ed;
--auth-ui-username-password-bg: rgb(93, 57, 57);
--auth-ui-email-bg: rgb(71, 71, 112);
} }

View File

@ -0,0 +1,127 @@
/**
* GHColors theme by Avi Aryan (http://aviaryan.in)
* Inspired by Github syntax coloring
*/
code[class*="language-"],
pre[class*="language-"] {
color: #393a34;
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
"Liberation Mono", "Courier New", monospace;
direction: ltr;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
font-size: 0.9em;
line-height: 1.2em;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre > code[class*="language-"] {
font-size: 1em;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
background: #b3d4fc;
}
/* Code blocks */
pre[class*="language-"] {
/* padding: 1em; */
/* margin: 0.5em 0; */
overflow: auto;
/* border: 1px solid #dddddd; */
background-color: #f6f8fa;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: 0.2em;
padding-top: 1px;
padding-bottom: 1px;
background: #f8f8f8;
border: 1px solid #dddddd;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999988;
font-style: italic;
}
.token.namespace {
opacity: 0.7;
}
.token.string,
.token.attr-value {
color: #e3116c;
}
.token.punctuation,
.token.operator {
color: #393a34; /* no highlight */
}
.token.entity,
.token.url,
.token.symbol,
.token.number,
.token.boolean,
.token.variable,
.token.constant,
.token.property,
.token.regex,
.token.inserted {
color: #36acaa;
}
.token.atrule,
.token.keyword,
.token.attr-name,
.language-autohotkey .token.selector {
color: #00009f;
}
.token.function,
.token.deleted,
.language-autohotkey .token.tag {
color: #9a050f;
}
.token.tag,
.token.selector,
.language-autohotkey .token.keyword {
color: #00009f;
}
.token.important,
.token.function,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}

26
web/src/prism/prisma.js Normal file
View File

@ -0,0 +1,26 @@
// Taken from the Prisma docs
module.exports = (Prism) => {
Prism.languages.prisma = Prism.languages.extend("clike", {
keyword: /\b(?:datasource|enum|generator|model|type|view)\b/,
"type-class-name": /(\s+)[A-Z]\w+/, ///(\b)(\s+)[A-Z]\w+/
});
Prism.languages.javascript["class-name"][0].pattern =
/(\b(?:model|datasource|enum|generator|type)\s+)[\w.\\]+/;
Prism.languages.insertBefore("prisma", "function", {
annotation: {
pattern: /(^|[^.])@+\w+/,
lookbehind: true,
alias: "punctuation",
},
});
Prism.languages.insertBefore("prisma", "punctuation", {
"type-args": /\b(?:references|fields|onDelete|onUpdate):/,
});
Prism.languages.insertBefore("prisma", "type-class-name", {
"not-class": /\n(\s+)[A-Z]\w+/,
});
};

83
web/src/prism/wasp.js Normal file
View File

@ -0,0 +1,83 @@
// Converted from the TextMate definition at https://github.com/wasp-lang/vscode-wasp/blob/main/syntaxes/wasp.tmLanguage.yaml
module.exports = (Prism) => {
Prism.languages.wasp = {
"prisma-closure": {
pattern: /{=psl[\s\S]*?psl=}/,
inside: {
prisma: {
pattern: /[\s\S]+/,
inside: Prism.languages.prisma,
},
},
},
comment: {
pattern: /\/\/.*|\/\*[\s\S]*?\*\//,
greedy: true,
},
"json-closure": {
pattern: /{=json[\s\S]*?json=}/,
inside: {
punctuation: /[{}[\],]/,
property: {
pattern: /(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?=\s*:)/,
lookbehind: true,
greedy: true,
},
string: {
pattern: /(^|[^\\])"(?:\\.|[^\\"\r\n])*"(?!\s*:)/,
lookbehind: true,
greedy: true,
},
number: /-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/i,
operator: /:/,
boolean: /\b(?:false|true)\b/,
null: {
pattern: /\bnull\b/,
alias: "keyword",
},
},
},
"js-import": {
pattern: /import.*",?/,
inside: Prism.languages.javascript,
},
string: {
pattern: /"(?:\\.|[^\\"\r\n])*"/,
greedy: true,
},
number: /-?\d+(?:\.\d+)?/,
boolean: /\b(?:true|false)\b/,
enum: {
pattern:
/\b(EmailAndPassword|PostgreSQL|SQLite|Simple|PgBoss|SMTP|SendGrid|Mailgun)\b/,
alias: "constant",
},
"dict-key": {
pattern: /[a-zA-Z]+(?=:)/,
alias: "plain",
},
"declaration-type": {
pattern: /\b(action|apiNamespace|api|app|entity|job|page|query|route)\b/,
alias: "keyword",
},
"class-name": {
pattern: /[a-zA-Z][0-9a-zA-Z]*/,
alias: "variable",
},
"http-method": {
pattern: /\b(ALL|GET|POST|PUT|DELETE)\b/,
alias: "constant",
},
array: {
pattern: /\[[\s\S]*?\]/,
inside: {
punctuation: /[{}[\],]/,
value: {
pattern: /[^,\s\]]+/,
alias: "variable",
},
},
},
punctuation: /[{}[\],]/,
};
};

View File

@ -0,0 +1,24 @@
// This file gets auto-generated when you "eject" to add custom languages to Docosaurus
// We use it to add support for Prisma and Wasp syntax highlighting
import siteConfig from "@generated/docusaurus.config";
export default function prismIncludeLanguages(PrismObject) {
const {
themeConfig: { prism },
} = siteConfig;
const { additionalLanguages } = prism;
// Prism components work on the Prism instance on the window, while prism-
// react-renderer uses its own Prism instance. We temporarily mount the
// instance onto window, import components to enhance it, then remove it to
// avoid polluting global namespace.
// You can mutate PrismObject: registering plugins, deleting languages... As
// long as you don't re-assign it
globalThis.Prism = PrismObject;
additionalLanguages.forEach((lang) => {
// eslint-disable-next-line global-require, import/no-dynamic-require
require(`prismjs/components/prism-${lang}`);
});
require("./prism-prisma");
require("./prism-wasp");
delete globalThis.Prism;
}

View File

@ -0,0 +1 @@
require("../prism/prisma")(Prism);

View File

@ -0,0 +1 @@
require("../prism/wasp")(Prism);