mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-24 03:35:17 +03:00
commit
19b931c935
@ -50,16 +50,16 @@ entity Task {=psl
|
||||
psl=}
|
||||
|
||||
query getTasks {
|
||||
fn: import { getTasks } from "@server/queries",
|
||||
fn: import { getTasks } from "@server/queries.js",
|
||||
entities: [Task]
|
||||
}
|
||||
|
||||
action createTask {
|
||||
fn: import { createTask } from "@server/actions",
|
||||
fn: import { createTask } from "@server/actions.js",
|
||||
entities: [Task]
|
||||
}
|
||||
|
||||
action updateTask {
|
||||
fn: import { updateTask } from "@server/actions",
|
||||
fn: import { updateTask } from "@server/actions.js",
|
||||
entities: [Task]
|
||||
}
|
||||
|
@ -45,10 +45,10 @@ get by running `wasp new project` from this point onwards):
|
||||
|
||||
Main differences:
|
||||
- All server-side code must be located inside the `src/server` directory. Wasp
|
||||
declarations must import this code with `import foo from "@server/foo"`
|
||||
declarations must import this code with `import foo from "@server/foo.js"`
|
||||
(instead of `import foo from "@ext/foo.js"`)
|
||||
- All client-side code must be located inside the `src/client` directory. Wasp
|
||||
declarations must import this code with `import foo from "@client/bar"`
|
||||
declarations must import this code with `import foo from "@client/bar.js"`
|
||||
(instead of `import bar from "@ext/bar.js"`)
|
||||
- All shared code (i.e., used on both the client and the server) must be
|
||||
located inside the `src/shared` and imported where needed through a relative import.
|
||||
@ -107,7 +107,7 @@ directory `foo`, you should:
|
||||
|
||||
query getTasks {
|
||||
// This resolves to src/server/queries.js
|
||||
fn: import { getTasks } from "@server/queries",
|
||||
fn: import { getTasks } from "@server/queries.js",
|
||||
}
|
||||
```
|
||||
Do this for all external imports in your `.wasp` file. After you're done, there shouldn't be any occurences of the string `"@ext"`.
|
||||
|
@ -76,7 +76,7 @@ entity User {=psl
|
||||
psl=}
|
||||
```
|
||||
|
||||
Check [here](https://github.com/wasp-lang/wasp/blob/main/examples/tutorials/TodoApp/main.wasp) for the complete example.
|
||||
Check [here](https://github.com/wasp-lang/wasp/blob/release/examples/tutorials/TodoApp/main.wasp) for the complete example.
|
||||
|
||||
## Why a language (DSL), aren’t frameworks solving this already?
|
||||
|
||||
|
@ -13,7 +13,7 @@ import ImgWithCaption from './components/ImgWithCaption'
|
||||
![Enter Waspello](../static/img/waspello-screenshot.png)
|
||||
|
||||
<p align="center">
|
||||
<Link to={'https://waspello.netlify.app/'}>Try Waspello here!</Link> | <Link to={'https://github.com/wasp-lang/wasp/blob/main/examples/waspello/main.wasp'}>See the code</Link>
|
||||
<Link to={'https://waspello.netlify.app/'}>Try Waspello here!</Link> | <Link to={'https://github.com/wasp-lang/wasp/blob/release/examples/waspello/main.wasp'}>See the code</Link>
|
||||
</p>
|
||||
|
||||
We've built a Trello clone using Wasp! Read on to learn how it went and how you can contribute.
|
||||
@ -127,7 +127,7 @@ export const getListsAndCards = async (args, context) => {
|
||||
```
|
||||
This is just a regular Node.js function, there are no limits on what you can return! All the stuff provided by Wasp (user data, Prisma SDK for a specific entity) comes in a `context` variable.
|
||||
|
||||
The code for actions is very similar (we just need to use `action` keyword instead of `query`) so I won't repeat it here. You can check out the code for `updateCard` action [here](https://github.com/wasp-lang/wasp/blob/main/examples/waspello/main.wasp#L103).
|
||||
The code for actions is very similar (we just need to use `action` keyword instead of `query`) so I won't repeat it here. You can check out the code for `updateCard` action [here](https://github.com/wasp-lang/wasp/blob/release/examples/waspello/main.wasp#L103).
|
||||
|
||||
#### Pages, routing & components
|
||||
To display all the nice data we have, we'll use React components. There are no limits to how you can use React components within Wasp, the only one is that each `page` has its root component:
|
||||
|
@ -14,7 +14,7 @@ import ImgWithCaption from './components/ImgWithCaption'
|
||||
![Hello, Waspleau](../static/img/waspleau-screenshot.png)
|
||||
|
||||
<p align="center">
|
||||
<Link to={'https://waspleau.netlify.app/'}>See Waspleau here!</Link> | <Link to={'https://github.com/wasp-lang/wasp/blob/main/examples/waspleau'}>See the code</Link>
|
||||
<Link to={'https://waspleau.netlify.app/'}>See Waspleau here!</Link> | <Link to={'https://github.com/wasp-lang/wasp/blob/release/examples/waspleau'}>See the code</Link>
|
||||
</p>
|
||||
|
||||
We've built a dashboard powered by a job queue using Wasp!
|
||||
@ -124,7 +124,7 @@ const workerFunction = async (opts) => {
|
||||
export const githubWorker = { name: 'GitHub API', fn: workerFunction, schedule: '*/10 * * * *' }
|
||||
```
|
||||
|
||||
_Note: Please see the [actual serverSetup.js file](https://github.com/wasp-lang/wasp/blob/main/examples/waspleau/src/server/serverSetup.js) for how we use this abstraction in practice._
|
||||
_Note: Please see the [actual serverSetup.js file](https://github.com/wasp-lang/wasp/blob/release/examples/waspleau/src/server/serverSetup.js) for how we use this abstraction in practice._
|
||||
|
||||
### Server → client
|
||||
|
||||
@ -165,7 +165,7 @@ const { data: dashboardData, isFetching, error } = useQuery(refreshDashboardData
|
||||
|
||||
## Congratulations, let’s dance!
|
||||
|
||||
Whew, we did it! If you’d like to deploy your own customized version of this dashboard, please clone [our repo](https://github.com/wasp-lang/wasp) and check out the Waspleau example [README.md](https://github.com/wasp-lang/wasp/blob/main/examples/waspleau/README.md) for tips on getting started. You can also [check out our docs](https://wasp-lang.dev/docs) to dive deeper into anything.
|
||||
Whew, we did it! If you’d like to deploy your own customized version of this dashboard, please clone [our repo](https://github.com/wasp-lang/wasp) and check out the Waspleau example [README.md](https://github.com/wasp-lang/wasp/blob/release/examples/waspleau/README.md) for tips on getting started. You can also [check out our docs](https://wasp-lang.dev/docs) to dive deeper into anything.
|
||||
|
||||
![Rickroll](../static/img/waspleau-rickroll.gif)
|
||||
|
||||
|
@ -144,7 +144,7 @@ For those interested, check out the [full diff here](https://github.com/wasp-lan
|
||||
|
||||
## Looks neat! What’s next?
|
||||
|
||||
First off, please check out our docs for Jobs: [https://wasp-lang.dev/docs/language/features#jobs](https://wasp-lang.dev/docs/language/features#jobs) There, you will find all the info you need to start using them. Next, if you want to see the code for this example in full, you can find it here: [https://github.com/wasp-lang/wasp/tree/main/examples/waspleau](https://github.com/wasp-lang/wasp/tree/main/examples/waspleau)
|
||||
First off, please check out our docs for Jobs: [https://wasp-lang.dev/docs/language/features#jobs](https://wasp-lang.dev/docs/language/features#jobs) There, you will find all the info you need to start using them. Next, if you want to see the code for this example in full, you can find it here: [https://github.com/wasp-lang/wasp/tree/release/examples/waspleau](https://github.com/wasp-lang/wasp/tree/release/examples/waspleau)
|
||||
|
||||
In the future, we plan to add more job executors, including support for polyglot workers (imagine running your Python ML function from Wasp!). We are also open to any other ideas on how jobs can become more useful to you (like client-side access to server-side jobs, or client-side jobs using similar abstractions?). Let us know what you think!
|
||||
|
||||
|
@ -23,7 +23,7 @@ We’ll use Michele Gerarduzzi’s [open-source project](https://github.com/mich
|
||||
- Building an app shouldn’t take more than 15 minutes.
|
||||
- Use modern web dev technologies (NodeJS + React)
|
||||
|
||||
As a result – we’ll get a simple and fun pet project. You can find the complete codebase [here](https://github.com/wasp-lang/wasp/tree/main/examples/tutorials/ItWaspsOnMyMachine).
|
||||
As a result – we’ll get a simple and fun pet project. You can find the complete codebase [here](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/ItWaspsOnMyMachine).
|
||||
|
||||
![Final result](../static/img/final-excuse-app.png)
|
||||
|
||||
|
137
web/blog/2022-11-28-why-we-chose-prisma.md
Normal file
137
web/blog/2022-11-28-why-we-chose-prisma.md
Normal file
@ -0,0 +1,137 @@
|
||||
---
|
||||
title: 'Why we chose Prisma as a database layer for Wasp'
|
||||
authors: [martinsos]
|
||||
image: /img/why-we-chose-prisma/wasp-loves-prisma.png
|
||||
tags: [webdev, wasp, prisma]
|
||||
---
|
||||
|
||||
import Link from '@docusaurus/Link';
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
|
||||
import InBlogCta from './components/InBlogCta';
|
||||
import WaspIntro from './_wasp-intro.md';
|
||||
import ImgWithCaption from './components/ImgWithCaption'
|
||||
|
||||
<ImgWithCaption
|
||||
alt="Beta is coming"
|
||||
source="img/why-we-chose-prisma/wasp-loves-prisma.png"
|
||||
/>
|
||||
|
||||
Wasp is a full-stack JS web dev framework, covering frontend, backend, and database. When choosing the solution to build our database layer on top, we chose Prisma, even though it was still somehwat new tech at that point, and we believe today we made a great choice -> read on to learn why!
|
||||
|
||||
<!--truncate-->
|
||||
|
||||
At Wasp, [we aim](/docs/vision) to simplify full-stack web development via a specialized high-level language. This language allows you to describe the main parts of your web app succinctly, avoiding a lot of usual boilerplate and configuration while giving you lots of features and ensuring best practices. Wasp is essentially a full-stack web framework implemented as a specialized language that works with React & Node.js!
|
||||
|
||||
When we started working on Wasp, we wanted to keep it easy to learn and to the point, so we decided:
|
||||
|
||||
- the Wasp language should only be used at a high level, so you would still use React, NodeJS, HTML, CSS, etc. to implement your custom logic. If a full-stack web app is an orchestra, Wasp is the conductor.
|
||||
- the Wasp language should be declarative and simple, very similar to JSON, but “smarter” in the sense it understands web app concepts and makes sure your app follows them.
|
||||
|
||||
With that in mind, we focused on identifying high-level web app concepts that are worth capturing in the Wasp language. We identified the following parts of a web app:
|
||||
|
||||
- General app info (title, head, favicon, …)
|
||||
- Pages and Routes
|
||||
- Data Models (aka Entities), e.g. User, Task, Organization, Article, … .
|
||||
- Operations (communication between client and server; CRUD on data models, 3rd party APIs, …)
|
||||
- Deployment
|
||||
|
||||
## Entities
|
||||
|
||||
Of all of those, Entities are in the middle of everything, present through the whole codebase, and are central to all the other parts of the web app: client, server, and database. They were, however, also the most daunting part to implement!
|
||||
|
||||
When we started, we imagined an Entity would look something like this in Wasp:
|
||||
|
||||
```
|
||||
entity User {
|
||||
id: Id,
|
||||
username: String @unique,
|
||||
email: String @unique
|
||||
groups: [Group]
|
||||
}
|
||||
```
|
||||
|
||||
While adding this initial syntax to our language was feasible, there were also much bigger tasks to tackle in order to make this a proper solution:
|
||||
|
||||
- expand syntax to be flexible enough for real-life use cases
|
||||
- support migrations (data and schema)
|
||||
- generate code that users can call from JS/TS to query and update entities in the DB
|
||||
- and probably a lot of other things that we hadn’t even thought of yet!
|
||||
|
||||
## Mongoose, Sequelize, … or Prisma?
|
||||
|
||||
We already decided that we would pick an ORM(ish) solution for JS/TS which we would build the rest of the features on top of. We started evaluating different ones: Mongoose, Sequelize, TypeORM, … .
|
||||
|
||||
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:
|
||||
|
||||
```
|
||||
model User {
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
password String
|
||||
}
|
||||
```
|
||||
|
||||
No, this is not another idea of how the syntax for Entities could look like in Wasp language → this is the Prisma Schema Language (PSL)!!!
|
||||
|
||||
## Prisma Schema Language (PSL)
|
||||
|
||||
Indeed, Prisma is unique in having a special, declarative language for describing data models (schema), and it was exactly what we needed for Wasp.
|
||||
|
||||
So instead of implementing our own syntax for describing Entities, we decided to use Prisma and their PSL to describe Entities (data models) inside the Wasp language.
|
||||
|
||||
Today, Entities are described like this in Wasp language:
|
||||
|
||||
```
|
||||
... some Wasp code ...
|
||||
|
||||
entity User {=psl
|
||||
id Int @id @default(autoincrement())
|
||||
username String @unique
|
||||
password String
|
||||
psl=}
|
||||
|
||||
... some Wasp code ...
|
||||
```
|
||||
|
||||
So in the middle of Wasp, you just switch to writing PSL (Prisma Schema Language) to describe an entity!
|
||||
|
||||
Another great thing is that the PSL is at its core a pretty simple language, so we [implemented our own parser](https://github.com/wasp-lang/wasp/blob/main/waspc/src/Wasp/Psl/Parser/Model.hs) for it → that means that Wasp actually understands what you wrote, even though it is PSL, and can fully work with it. So we lost nothing by using PSL instead of our own syntax and instead gained all the features that Prisma brings.
|
||||
|
||||
## Other Benefits
|
||||
|
||||
Besides PSL, there were plenty of other reasons why we felt Prisma is a great fit for us:
|
||||
|
||||
- It is targeting Javascript / Typescript.
|
||||
- It takes care of migrations and has a nice workflow for doing it.
|
||||
- It supports different databases: Mongo, PostgreSQL, CockroachDB, …, which is very important for Wasp since our vision is to support different stacks in the future.
|
||||
- It has Prisma Studio - UI for inspecting your database, which we also make available to you via Wasp CLI.
|
||||
- It keeps improving quickly and is very focused on a nice developer experience, which is also our focus here at Wasp.
|
||||
- Community is extremely welcoming and the core team is super helpful - all of our questions and issues were answered super quickly!
|
||||
|
||||
## Challenges
|
||||
|
||||
While integrating Prisma into Wasp went really smoothly, there were a few hiccups:
|
||||
|
||||
- Getting Prisma CLI to provide interactive output while being called programmatically by Wasp was tricky, and in the end, we had to use a bit of a dirty approach to trick the Prisma CLI into thinking it is called interactively. We opened an issue for this with Prisma, so hopefully, we will be able to remove this once it is resolved: https://github.com/prisma/prisma/issues/7113.
|
||||
- In the early days, there were some bugs, however, they were always quickly solved, so updating to the newest Prisma version was often the solution.
|
||||
- It took us a bit of fiddling to get Prisma to work with its schema outside of the server’s root directory, but we did get it working in the end!
|
||||
|
||||
Most of these were due to us stretching the boundaries of how Prisma was imagined to be used, but in total Prisma proved to be fairly flexible!
|
||||
|
||||
## Summary
|
||||
|
||||
With its declarative language for describing schema, focus on ergonomics, and JS/TS as the target language, Prisma was really a stroke of luck for us - if not for it, it would have taken much more effort to get the Entities working in Wasp.
|
||||
|
||||
When we started using it, Prisma was still somewhat early, and it was certainly the least-mature technology in our stack - but we decided to bet on it because it was just a perfect fit, and it made so much sense. Today, with Prisma being a mature and popular solution, we are more than happy we made that choice!
|
||||
|
||||
## Future
|
||||
|
||||
Already, Prisma is playing a big role at Wasp, but there is still more that we plan and want to do:
|
||||
|
||||
- support Prisma’s Enum and Type declarations
|
||||
- expose more of Prisma’s CLI commands, especially database seeding
|
||||
- add support in Wasp for multiple databases (which Prisma already supports)
|
||||
- improve IDE support for PSL within the Wasp language
|
||||
|
||||
If you are interested in helping with any of these, reach out to us on this issue https://github.com/wasp-lang/wasp/issues/641, or in any case, join us on our [Discord server](https://discord.gg/rzdnErX)!
|
@ -3,8 +3,8 @@ title: Examples
|
||||
---
|
||||
|
||||
Todo App:
|
||||
- source code: https://github.com/wasp-lang/wasp/tree/main/examples/tutorials/TodoApp .
|
||||
- source code: https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/TodoApp .
|
||||
|
||||
Real World App (clone of Medium):
|
||||
- source code: https://github.com/wasp-lang/wasp/tree/main/examples/realworld .
|
||||
- source code: https://github.com/wasp-lang/wasp/tree/release/examples/realworld .
|
||||
- hosted at: https://wasp-rwa.netlify.app/ .
|
||||
|
@ -94,6 +94,10 @@ With Wasp for Windows, we are almost there: Wasp is successfully compiling and r
|
||||
|
||||
In the meantime, the best way to start using Wasp on Windows is by using [WSL](https://docs.microsoft.com/en-us/windows/wsl/install-win10). Once you set up Ubuntu on WSL, just follow Linux instructions for installing Wasp. If you need further help, reach out to us on [Discord](https://discord.gg/rzdnErX) - we have some community members using WSL that might be able to help you.
|
||||
|
||||
:::caution
|
||||
If you are using WSL2, make sure that your Wasp project is not on Windows file system, but instead on Linux file system. Otherwise, Wasp won't be able to detect file changes, due to the [issue in WSL2](https://github.com/microsoft/WSL/issues/4739).
|
||||
:::
|
||||
|
||||
</div>
|
||||
</TabItem>
|
||||
|
||||
@ -140,7 +144,8 @@ The extension brings the following functionality:
|
||||
|
||||
## 4. What next?
|
||||
|
||||
**Check out the 🤓 [Pick a Tutorial page](pick-a-tutorial.md) 🤓. Choose an app that you'd like to build and it will take you through all the core features of Wasp!**
|
||||
**Check out the 🤓 [Todo App tutorial](tutorials/todo-app.md) 🤓 , which will take you through all the core features of Wasp!**
|
||||
|
||||
Also, we would be excited to have you **join our community on [Discord](https://discord.gg/rzdnErX)!** Any feedback or questions you have, we are there for you.
|
||||
|
||||
Finally, to stay up to date with updates in Wasp, you can **subscribe to our newsletter: https://wasp-lang.dev/#signup ** . We usually send 1 per month, and Matija does his best to unleash his creativity to make them engaging and fun to read :D!
|
||||
|
@ -1,32 +0,0 @@
|
||||
---
|
||||
title: Pick a Tutorial
|
||||
---
|
||||
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl';
|
||||
import DiscordLink from '../blog/components/DiscordLink';
|
||||
|
||||
Congrats, you're now familiar with the basic concepts of Wasp! 🥳
|
||||
|
||||
It's time to build something cool and check the capabilities of Wasp in action. Pick one of the tutorials below and let us know your impressions on <DiscordLink />!
|
||||
|
||||
### To-Do app
|
||||
|
||||
[The To-Do app](tutorials/todo-app) is a thorough journey covering most of the Wasp's concepts, from very basics to features like auth, dependency management, operations, ... . If you'd like to get familiar with Wasp on a more detailed level - this is a tutorial for you!
|
||||
|
||||
Time estimate: ~45 minutes
|
||||
|
||||
### Dev excuses app
|
||||
|
||||
[Dev excuses app](tutorials/dev-excuses-app) is a fun, quick overview of how you can build a full-stack app with Wasp in a matter of minutes. Do not expect any detailed concept explanations, and refer to this tutorial if you'd like to get a fast overview of Wasp's possibilities.
|
||||
|
||||
Time estimate: ~20 minutes
|
||||
|
||||
<hr/>
|
||||
|
||||
<img alt="Let's build!"
|
||||
src={useBaseUrl('img/develop-an-app.jpg')}
|
||||
style={{ border: "1px solid black" }}
|
||||
height="400px"
|
||||
/>
|
||||
|
||||
P.S: If you decided to build an app on your own - we'd love to see it! If it's simple enough, let's turn it into a tutorial! Please check our [contribution guide](contributing) for detailed instructions and reach us on Discord.
|
@ -18,7 +18,7 @@ We’ll use Michele Gerarduzzi’s [open-source project](https://github.com/mich
|
||||
- Building an app shouldn’t take more than 15 minutes.
|
||||
- Use modern web dev technologies (NodeJS + React)
|
||||
|
||||
As a result – we’ll get a simple and fun pet project. You can find the complete codebase [here](https://github.com/wasp-lang/wasp/tree/main/examples/tutorials/ItWaspsOnMyMachine).
|
||||
As a result – we’ll get a simple and fun pet project. You can find the complete codebase [here](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/ItWaspsOnMyMachine).
|
||||
|
||||
<img alt="Final result"
|
||||
src={useBaseUrl('img/dev-excuses-live-preview.gif')}
|
||||
|
@ -22,7 +22,7 @@ This tutorial will take you step by step through the most important features of
|
||||
|
||||
If you get stuck at any point (or just want to chat), reach out to us on [Discord](https://discord.gg/rzdnErX) and we will help you!
|
||||
|
||||
You can check out the complete code of the app we are about to build with Wasp [here](https://github.com/wasp-lang/wasp/tree/main/examples/tutorials/TodoApp).
|
||||
You can check out the complete code of the app we are about to build with Wasp [here](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/TodoApp).
|
||||
|
||||
:::tip
|
||||
If you are interested at any moment in what is Wasp actually generating in the background, take a look at `.wasp/out/` directory in your project.
|
||||
|
@ -32,12 +32,17 @@ query getTasks {
|
||||
// We specify that JS implementation of the query (which is an async JS function)
|
||||
// can be found in `src/server/queries.js` as the named export `getTasks`.
|
||||
// Use '@server' to reference files inside the src/server folder.
|
||||
fn: import { getTasks } from "@server/queries",
|
||||
fn: import { getTasks } from "@server/queries.js",
|
||||
// We tell Wasp that this query is doing something with entity `Task`. With that, Wasp will
|
||||
// automatically refresh the results of this query when tasks change.
|
||||
entities: [Task]
|
||||
}
|
||||
```
|
||||
:::caution
|
||||
Even if you use TypeScript and have the file `queries.ts`, you will still need to import it using the `.js` extension. Wasp internally uses `esnext` module resultion, which always requires specifying the extension as `.js` (i.e., the extension used in the emitted JS file). This applies to all `@server` immports (and files on the server in general). It does not apply to client files.
|
||||
|
||||
Read more about ES modules in TypeScript [here](https://www.typescriptlang.org/docs/handbook/esm-node.html). If you're interested in the discussion and the reasoning behind this, read about it [in this GitHub issue](https://github.com/microsoft/TypeScript/issues/33588).
|
||||
:::
|
||||
|
||||
### JS implementation
|
||||
Next, create a new file `src/server/queries.js` and define the JS we've just used in the `query` declaration above:
|
||||
|
@ -19,7 +19,7 @@ First we declare the action in Wasp:
|
||||
// ...
|
||||
|
||||
action createTask {
|
||||
fn: import { createTask } from "@server/actions",
|
||||
fn: import { createTask } from "@server/actions.js",
|
||||
entities: [Task]
|
||||
}
|
||||
```
|
||||
|
@ -20,7 +20,7 @@ We declare a Wasp action:
|
||||
// ...
|
||||
|
||||
action updateTask {
|
||||
fn: import { updateTask } from "@server/actions",
|
||||
fn: import { updateTask } from "@server/actions.js",
|
||||
entities: [Task]
|
||||
}
|
||||
```
|
||||
|
@ -12,7 +12,7 @@ We did it! For all those that followed the instructions closely and created "Bui
|
||||
style={{ border: '1px solid black' }}
|
||||
/>
|
||||
|
||||
You can check out the whole code of the app that we just built [here](https://github.com/wasp-lang/wasp/tree/main/examples/tutorials/TodoApp).
|
||||
You can check out the whole code of the app that we just built [here](https://github.com/wasp-lang/wasp/tree/release/examples/tutorials/TodoApp).
|
||||
|
||||
If you are interested in what is Wasp actually generating in the background, you can check the `.wasp/out/` directory in your project.
|
||||
|
||||
@ -30,3 +30,5 @@ Or, you could use Wasp to build something of your own!
|
||||
If you notice that some of the features you'd like to have are missing, or have any other kind of feedback, please write to us on [Discord](https://discord.gg/rzdnErX) or create an issue on [Github](https://github.com/wasp-lang/wasp), so we can learn which features to add/improve next.
|
||||
Even better, if you would like to contribute or help building the feature, let us know!
|
||||
You can find more details on contributing [here](contributing.md).
|
||||
|
||||
Oh, and do **subscribe to our newsletter: https://wasp-lang.dev/#signup ** ! We usually send 1 per month, and Matija does his best to unleash his creativity to make them engaging and fun to read :D!
|
||||
|
@ -20,9 +20,10 @@ module.exports = {
|
||||
],
|
||||
themeConfig: {
|
||||
announcementBar: {
|
||||
id: 'Beta_is_coming',
|
||||
content: '<strong>We are releasing Beta on Nov 27! 🚀 Sign up <a href="#signup">here</a> to get notified. 🔔</strong>',
|
||||
backgroundColor: '#ffcc00',
|
||||
id: 'Beta_is_here',
|
||||
content: 'Wasp Beta is live on Product Hunt 🚀 <strong><a href="https://www.producthunt.com/posts/wasp-lang-beta">Support us now!</a></strong>',
|
||||
backgroundColor: '#ff6154',
|
||||
textColor: '#fff',
|
||||
isCloseable: false,
|
||||
},
|
||||
navbar: {
|
||||
|
45
web/package-lock.json
generated
45
web/package-lock.json
generated
@ -23,6 +23,7 @@
|
||||
"react-feather": "^2.0.10",
|
||||
"react-modal": "^3.14.3",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -5462,6 +5463,15 @@
|
||||
"utila": "~0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
@ -9834,6 +9844,21 @@
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.6.0",
|
||||
"react-dom": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
@ -16717,6 +16742,15 @@
|
||||
"utila": "~0.4"
|
||||
}
|
||||
},
|
||||
"dom-helpers": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
|
||||
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.8.7",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
@ -19794,6 +19828,17 @@
|
||||
"use-latest": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"react-transition-group": {
|
||||
"version": "4.4.5",
|
||||
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
|
||||
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.5.5",
|
||||
"dom-helpers": "^5.0.1",
|
||||
"loose-envify": "^1.4.0",
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"read-cache": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
|
||||
|
@ -29,6 +29,7 @@
|
||||
"react-feather": "^2.0.10",
|
||||
"react-modal": "^3.14.3",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"react-transition-group": "^4.4.5",
|
||||
"tailwindcss": "^3.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
@ -8,7 +8,6 @@ module.exports = {
|
||||
'getting-started',
|
||||
'about',
|
||||
'how-it-works',
|
||||
'pick-a-tutorial'
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -39,19 +38,6 @@ module.exports = {
|
||||
'tutorials/todo-app/08-the-end'
|
||||
]
|
||||
},
|
||||
{
|
||||
type: 'category',
|
||||
label: 'Dev Excuses app',
|
||||
collapsed: true,
|
||||
items: [
|
||||
'tutorials/dev-excuses-app',
|
||||
'tutorials/dev-excuses-app/01-creating-the-project',
|
||||
'tutorials/dev-excuses-app/02-modifying-main-wasp-file',
|
||||
'tutorials/dev-excuses-app/03-adding-operations',
|
||||
'tutorials/dev-excuses-app/04-updating-main-page-js-file',
|
||||
'tutorials/dev-excuses-app/05-perform-migration-and-run',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -20,6 +20,7 @@ const faqs = [
|
||||
also need some kind of a server/backend if you'll need to run more complex operations.
|
||||
</p>
|
||||
},
|
||||
/*
|
||||
{
|
||||
question: 'How is Wasp different from Ruby on Rails, Django, etc?',
|
||||
answer: <p>
|
||||
@ -36,6 +37,7 @@ const faqs = [
|
||||
you under the hood.
|
||||
</p>
|
||||
},
|
||||
*/
|
||||
{
|
||||
question: 'How hard is it to learn Wasp?',
|
||||
answer: <p>
|
||||
|
@ -56,7 +56,7 @@ const ActionButtons = () => (
|
||||
|
||||
const PHBadge = () => (
|
||||
<a
|
||||
href="https://www.producthunt.com/posts/wasp-lang-alpha?utm_source=badge-top-post-badge&utm_medium=badge&utm_souce=badge-wasp-lang-alpha"
|
||||
href="https://www.producthunt.com/posts/wasp-lang-beta"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
@ -93,7 +93,7 @@ page MainPage {
|
||||
<SectionContainer className='pb-5 pt-24'>
|
||||
<div className='lg:grid lg:grid-cols-12 lg:gap-16'>
|
||||
|
||||
<div className='lg:col-span-6 space-y-12'>
|
||||
<div className='lg:col-span-6 space-y-12 z-10'>
|
||||
{/* Hero title and subtitle */}
|
||||
<div>
|
||||
<h1
|
||||
|
47
web/src/components/Nav/Announcement.js
Normal file
47
web/src/components/Nav/Announcement.js
Normal file
@ -0,0 +1,47 @@
|
||||
import React from 'react'
|
||||
import { ChevronRight, X } from 'react-feather'
|
||||
|
||||
const Announcement = () => {
|
||||
|
||||
const handleLink = () => {
|
||||
window.open('https://www.producthunt.com/posts/wasp-lang-beta')
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={handleLink}
|
||||
className={`
|
||||
overflow-hidden
|
||||
cursor-pointer flex-row
|
||||
space-x-3
|
||||
text-white
|
||||
bg-[#ff6154]
|
||||
`}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
mx-auto flex items-center justify-center divide-white p-3
|
||||
text-sm font-medium
|
||||
lg:container lg:divide-x lg:px-16 xl:px-20
|
||||
`}
|
||||
>
|
||||
<span className='item-center flex gap-2 px-3'>
|
||||
|
||||
<span>Wasp Beta is live on Product Hunt 🚀</span>
|
||||
</span>
|
||||
|
||||
<span className='hidden items-center space-x-2 px-3 lg:flex'>
|
||||
<span>Support us now!</span>
|
||||
<ChevronRight size={14} />
|
||||
</span>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
export default Announcement
|
@ -1,11 +1,15 @@
|
||||
import React from 'react'
|
||||
import React, { useState } from 'react'
|
||||
import Link from '@docusaurus/Link'
|
||||
import { Star, Twitter } from 'react-feather'
|
||||
|
||||
import Announcement from './Announcement'
|
||||
import Transition from '../../lib/Transition'
|
||||
import { DiscordIcon, TwitterIcon } from './SocialIcons'
|
||||
|
||||
const Nav = () => {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const Logo = () => (
|
||||
<div className='flex flex-shrink-0 items-center'>
|
||||
<Link to='/'>
|
||||
@ -57,8 +61,11 @@ const Nav = () => {
|
||||
</a>
|
||||
)
|
||||
|
||||
const HamburgerButton = () => (
|
||||
<div className='absolute inset-y-0 left-0 px-2 flex items-center lg:hidden'>
|
||||
const HamburgerButton = ({ toggleFlyOut }) => (
|
||||
<div
|
||||
className='absolute inset-y-0 left-0 px-2 flex items-center lg:hidden'
|
||||
onClick={() => toggleFlyOut()}
|
||||
>
|
||||
<button
|
||||
className={`
|
||||
inline-flex items-center p-2
|
||||
@ -91,6 +98,7 @@ const Nav = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Announcement />
|
||||
<div className='sticky top-0 z-50'>
|
||||
<div className='bg-[#f5f4f0] absolute top-0 h-full w-full opacity-80'></div>
|
||||
<nav className='border-b backdrop-blur-sm'>
|
||||
@ -101,7 +109,7 @@ const Nav = () => {
|
||||
lg:container lg:px-16 xl:px-20
|
||||
'
|
||||
>
|
||||
<HamburgerButton />
|
||||
<HamburgerButton toggleFlyOut={() => setOpen(true)} />
|
||||
<div className='
|
||||
flex flex-1
|
||||
items-center justify-center
|
||||
@ -200,10 +208,131 @@ const Nav = () => {
|
||||
url='https://twitter.com/WaspLang'
|
||||
/>
|
||||
</div> {/* EOF right side */}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Mobile Nav Menu */}
|
||||
<Transition
|
||||
appear={true}
|
||||
show={open}
|
||||
enter="transition ease-out duration-200"
|
||||
enterFrom="opacity-0 translate-y-1"
|
||||
enterTo="opacity-100 translate-y-0"
|
||||
leave="transition ease-in duration-150"
|
||||
leaveFrom="opacity-100 translate-y-0"
|
||||
leaveTo="opacity-0 translate-y-1"
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
fixed -inset-y-0 z-50 h-screen w-screen transform overflow-y-scroll bg-white p-4 md:p-8
|
||||
`}
|
||||
>
|
||||
<div className='absolute right-4 top-4 items-center justify-between'>
|
||||
<div className="-mr-2">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpen(false)}
|
||||
className={`
|
||||
text-neutral-700 inline-flex items-center justify-center rounded-md
|
||||
bg-white p-2 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset
|
||||
`}
|
||||
>
|
||||
<span className="sr-only">Close menu</span>
|
||||
<svg
|
||||
className="h-6 w-6"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M6 18L18 6M6 6l12 12"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-6 mb-12'>
|
||||
{/* Docs */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="/docs">
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
Docs
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Docs */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="/blog">
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
Blog
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* FAQ */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="#faq" onClick={() => setOpen(false)}>
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
FAQ
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Join the list */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="#signup" onClick={() => setOpen(false)}>
|
||||
<span
|
||||
className={`
|
||||
text-neutral-700 block pl-3 pr-4 text-base font-medium
|
||||
px-2 py-1 rounded bg-yellow-500/25 hover:bg-yellow-500/10
|
||||
`}>
|
||||
📬 Join the list
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* GitHub */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="https://github.com/wasp-lang/wasp">
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
⭐️ GitHub
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Discord */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="https://discord.gg/rzdnErX">
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
👾 Discord
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Twitter */}
|
||||
<div className='space-y-1 pt-2 pb-4'>
|
||||
<Link to="https://twitter.com/WaspLang">
|
||||
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
|
||||
🐦 Twitter
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
|
||||
|
108
web/src/lib/Transition.js
Normal file
108
web/src/lib/Transition.js
Normal file
@ -0,0 +1,108 @@
|
||||
// From: https://gist.github.com/adamwathan/3b9f3ad1a285a2d1b482769aeb862467
|
||||
|
||||
import { CSSTransition as ReactCSSTransition } from 'react-transition-group'
|
||||
import React, { useRef, useEffect, useContext } from 'react'
|
||||
|
||||
const TransitionContext = React.createContext({
|
||||
parent: {},
|
||||
})
|
||||
|
||||
function useIsInitialRender() {
|
||||
const isInitialRender = useRef(true)
|
||||
useEffect(() => {
|
||||
isInitialRender.current = false
|
||||
}, [])
|
||||
return isInitialRender.current
|
||||
}
|
||||
|
||||
function CSSTransition({
|
||||
show,
|
||||
enter = '',
|
||||
enterFrom = '',
|
||||
enterTo = '',
|
||||
leave = '',
|
||||
leaveFrom = '',
|
||||
leaveTo = '',
|
||||
appear,
|
||||
children,
|
||||
}) {
|
||||
const enterClasses = enter.split(' ').filter((s) => s.length)
|
||||
const enterFromClasses = enterFrom.split(' ').filter((s) => s.length)
|
||||
const enterToClasses = enterTo.split(' ').filter((s) => s.length)
|
||||
const leaveClasses = leave.split(' ').filter((s) => s.length)
|
||||
const leaveFromClasses = leaveFrom.split(' ').filter((s) => s.length)
|
||||
const leaveToClasses = leaveTo.split(' ').filter((s) => s.length)
|
||||
|
||||
function addClasses(node, classes) {
|
||||
classes.length && node.classList.add(...classes)
|
||||
}
|
||||
|
||||
function removeClasses(node, classes) {
|
||||
classes.length && node.classList.remove(...classes)
|
||||
}
|
||||
|
||||
return (
|
||||
<ReactCSSTransition
|
||||
appear={appear}
|
||||
unmountOnExit
|
||||
in={show}
|
||||
addEndListener={(node, done) => {
|
||||
node.addEventListener('transitionend', done, false)
|
||||
}}
|
||||
onEnter={(node) => {
|
||||
addClasses(node, [...enterClasses, ...enterFromClasses])
|
||||
}}
|
||||
onEntering={(node) => {
|
||||
removeClasses(node, enterFromClasses)
|
||||
addClasses(node, enterToClasses)
|
||||
}}
|
||||
onEntered={(node) => {
|
||||
removeClasses(node, [...enterToClasses, ...enterClasses])
|
||||
}}
|
||||
onExit={(node) => {
|
||||
addClasses(node, [...leaveClasses, ...leaveFromClasses])
|
||||
}}
|
||||
onExiting={(node) => {
|
||||
removeClasses(node, leaveFromClasses)
|
||||
addClasses(node, leaveToClasses)
|
||||
}}
|
||||
onExited={(node) => {
|
||||
removeClasses(node, [...leaveToClasses, ...leaveClasses])
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</ReactCSSTransition>
|
||||
)
|
||||
}
|
||||
|
||||
function Transition({ show, appear, ...rest }) {
|
||||
const { parent } = useContext(TransitionContext)
|
||||
const isInitialRender = useIsInitialRender()
|
||||
const isChild = show === undefined
|
||||
|
||||
if (isChild) {
|
||||
return (
|
||||
<CSSTransition
|
||||
appear={parent.appear || !parent.isInitialRender}
|
||||
show={parent.show}
|
||||
{...rest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<TransitionContext.Provider
|
||||
value={{
|
||||
parent: {
|
||||
show,
|
||||
isInitialRender,
|
||||
appear,
|
||||
},
|
||||
}}
|
||||
>
|
||||
<CSSTransition appear={appear} show={show} {...rest} />
|
||||
</TransitionContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
export default Transition
|
BIN
web/static/img/why-we-chose-prisma/wasp-loves-prisma.png
Normal file
BIN
web/static/img/why-we-chose-prisma/wasp-loves-prisma.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 49 KiB |
Loading…
Reference in New Issue
Block a user