mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-23 17:13:40 +03:00
Replace Wasp with AppSpec and Parser with Analyzer. (#423)
This commit is contained in:
parent
b74eb88ff0
commit
8d2c33ab28
@ -38,8 +38,8 @@ app TodoApp {
|
|||||||
title: "Todo App"
|
title: "Todo App"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/" -> page Main
|
route RootRoute { path: "/", to: MainPage }
|
||||||
page Main {
|
page MainPage {
|
||||||
component: import Main from "@ext/pages/Main.js" // Importing React component.
|
component: import Main from "@ext/pages/Main.js" // Importing React component.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,55 +3,64 @@ app Conduit {
|
|||||||
|
|
||||||
head: [
|
head: [
|
||||||
"<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap\" />"
|
"<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap\" />"
|
||||||
]
|
],
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
auth: {
|
||||||
userEntity: User,
|
userEntity: User,
|
||||||
methods: [ EmailAndPassword ],
|
methods: [ EmailAndPassword ],
|
||||||
onAuthFailedRedirectTo: "/login"
|
onAuthFailedRedirectTo: "/login"
|
||||||
|
},
|
||||||
|
|
||||||
|
db: { system: PostgreSQL },
|
||||||
|
|
||||||
|
dependencies: [
|
||||||
|
("prop-types", "15.7.2"),
|
||||||
|
("react-markdown", "5.0.3"),
|
||||||
|
("moment", "2.29.1"),
|
||||||
|
("@material-ui/core", "4.11.3"),
|
||||||
|
("@material-ui/icons", "4.11.2"),
|
||||||
|
("slug", "4.0.2")
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
db {
|
|
||||||
system: PostgreSQL
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------- Pages ------------------ //
|
// ----------------- Pages ------------------ //
|
||||||
|
|
||||||
route "/" -> page Main
|
route RootRoute { path: "/", to: MainPage }
|
||||||
page Main {
|
page MainPage {
|
||||||
component: import Main from "@ext/MainPage.js"
|
component: import Main from "@ext/MainPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/login" -> page LogIn
|
route LogInRoute { path: "/login", to: LogInPage }
|
||||||
page LogIn {
|
page LogInPage {
|
||||||
component: import LogIn from "@ext/auth/LoginPage.js"
|
component: import LogIn from "@ext/auth/LoginPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/register" -> page SignUp
|
route RegisterRoute { path: "/register", to: SignUpPage }
|
||||||
page SignUp {
|
page SignUpPage {
|
||||||
component: import SignUp from "@ext/auth/SignupPage.js"
|
component: import SignUp from "@ext/auth/SignupPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/settings" -> page UserSettings
|
route UserSettingsRoute { path: "/settings", to: UserSettingsPage }
|
||||||
page UserSettings {
|
page UserSettingsPage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import UserSettings from "@ext/user/components/UserSettingsPage.js"
|
component: import UserSettings from "@ext/user/components/UserSettingsPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/@:username" -> page UserProfile
|
route UserProfileRoute { path: "/@:username", to: UserProfilePage }
|
||||||
page UserProfile {
|
page UserProfilePage {
|
||||||
component: import UserProfile from "@ext/user/components/UserProfilePage.js"
|
component: import UserProfile from "@ext/user/components/UserProfilePage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/editor/:articleSlug?" -> page ArticleEditor
|
route ArticleEditorRoute { path: "/editor/:articleSlug?", to: ArticleEditorPage }
|
||||||
page ArticleEditor {
|
page ArticleEditorPage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import ArticleEditor from "@ext/article/components/ArticleEditorPage.js"
|
component: import ArticleEditor from "@ext/article/components/ArticleEditorPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/article/:articleSlug" -> page ArticleView
|
route ArticleViewRoute { path: "/article/:articleSlug", to: ArticleViewPage }
|
||||||
page ArticleView {
|
page ArticleViewPage {
|
||||||
component: import ArticleView from "@ext/article/components/ArticleViewPage.js"
|
component: import ArticleView from "@ext/article/components/ArticleViewPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,12 +199,3 @@ query getTags {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------- //
|
// -------------------------------------------- //
|
||||||
|
|
||||||
dependencies {=json
|
|
||||||
"prop-types": "15.7.2",
|
|
||||||
"react-markdown": "5.0.3",
|
|
||||||
"moment": "2.29.1",
|
|
||||||
"@material-ui/core": "4.11.3",
|
|
||||||
"@material-ui/icons": "4.11.2",
|
|
||||||
"slug": "4.0.2"
|
|
||||||
json=}
|
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
app Thoughts {
|
app Thoughts {
|
||||||
title: "Thoughts"
|
title: "Thoughts",
|
||||||
}
|
db: { system: PostgreSQL },
|
||||||
|
auth: {
|
||||||
db {
|
|
||||||
system: PostgreSQL
|
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
userEntity: User,
|
userEntity: User,
|
||||||
methods: [ EmailAndPassword ],
|
methods: [ EmailAndPassword ],
|
||||||
onAuthFailedRedirectTo: "/login"
|
onAuthFailedRedirectTo: "/login"
|
||||||
|
},
|
||||||
|
dependencies: [
|
||||||
|
("react-markdown", "6.0.1"),
|
||||||
|
("color-hash", "2.0.1")
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/" -> page Main
|
route MainRoute { path: "/", to: MainPage }
|
||||||
page Main {
|
page MainPage {
|
||||||
component: import Main from "@ext/MainPage.js",
|
component: import Main from "@ext/MainPage.js",
|
||||||
authRequired: true
|
authRequired: true
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/thoughts" -> page Thoughts
|
route ThoughtsRoute { path: "/thoughts", to: ThoughtsPage }
|
||||||
page Thoughts {
|
page ThoughtsPage {
|
||||||
component: import Thoughts from "@ext/ThoughtsPage.js",
|
component: import Thoughts from "@ext/ThoughtsPage.js",
|
||||||
authRequired: true
|
authRequired: true
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/login" -> page Login
|
route LoginRoute { path: "/login", to: LoginPage }
|
||||||
page Login {
|
page LoginPage {
|
||||||
component: import Login from "@ext/LoginPage.js"
|
component: import Login from "@ext/LoginPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/signup" -> page Signup
|
route SignupRoute { path: "/signup", to: SignupPage }
|
||||||
page Signup {
|
page SignupPage {
|
||||||
component: import Signup from "@ext/SignupPage"
|
component: import Signup from "@ext/SignupPage"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -80,8 +80,3 @@ entity User {=psl
|
|||||||
thoughts Thought[]
|
thoughts Thought[]
|
||||||
tags Tag[]
|
tags Tag[]
|
||||||
psl=}
|
psl=}
|
||||||
|
|
||||||
dependencies {=json
|
|
||||||
"react-markdown": "6.0.1",
|
|
||||||
"color-hash": "2.0.1"
|
|
||||||
json=}
|
|
@ -1,26 +1,30 @@
|
|||||||
app TodoApp {
|
app TodoApp {
|
||||||
title: "Todo app"
|
title: "Todo app",
|
||||||
}
|
|
||||||
|
|
||||||
auth {
|
auth: {
|
||||||
userEntity: User,
|
userEntity: User,
|
||||||
methods: [ EmailAndPassword ],
|
methods: [ EmailAndPassword ],
|
||||||
onAuthFailedRedirectTo: "/login"
|
onAuthFailedRedirectTo: "/login"
|
||||||
|
},
|
||||||
|
|
||||||
|
dependencies: [
|
||||||
|
("react-clock", "3.0.0")
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/" -> page Main
|
route RootRoute { path: "/", to: MainPage }
|
||||||
page Main {
|
page MainPage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import Main from "@ext/MainPage.js"
|
component: import Main from "@ext/MainPage.js"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/signup" -> page Signup
|
route SignupRoute { path: "/signup", to: SignupPage }
|
||||||
page Signup {
|
page SignupPage {
|
||||||
component: import Signup from "@ext/SignupPage"
|
component: import Signup from "@ext/SignupPage"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/login" -> page Login
|
route LoginRoute { path: "/login", to: LoginPage }
|
||||||
page Login {
|
page LoginPage {
|
||||||
component: import Login from "@ext/LoginPage"
|
component: import Login from "@ext/LoginPage"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +57,3 @@ action updateTask {
|
|||||||
fn: import { updateTask } from "@ext/actions.js",
|
fn: import { updateTask } from "@ext/actions.js",
|
||||||
entities: [Task]
|
entities: [Task]
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {=json
|
|
||||||
"react-clock": "3.0.0"
|
|
||||||
json=}
|
|
||||||
|
@ -1,57 +0,0 @@
|
|||||||
# Migration `20201023121126-a`
|
|
||||||
|
|
||||||
This migration has been generated by Martin Sosic at 10/23/2020, 2:11:26 PM.
|
|
||||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
|
||||||
|
|
||||||
## Database Steps
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE TABLE "User" (
|
|
||||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"email" TEXT NOT NULL,
|
|
||||||
"password" TEXT NOT NULL
|
|
||||||
)
|
|
||||||
|
|
||||||
CREATE TABLE "Task" (
|
|
||||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"description" TEXT NOT NULL,
|
|
||||||
"isDone" BOOLEAN NOT NULL DEFAULT false
|
|
||||||
)
|
|
||||||
|
|
||||||
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email")
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
```diff
|
|
||||||
diff --git schema.prisma schema.prisma
|
|
||||||
migration ..20201023121126-a
|
|
||||||
--- datamodel.dml
|
|
||||||
+++ datamodel.dml
|
|
||||||
@@ -1,0 +1,23 @@
|
|
||||||
+
|
|
||||||
+datasource db {
|
|
||||||
+ provider = "sqlite"
|
|
||||||
+ url = "***"
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+generator client {
|
|
||||||
+ provider = "prisma-client-js"
|
|
||||||
+ output = "../server/node_modules/.prisma/client"
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+model User {
|
|
||||||
+ id Int @id @default(autoincrement())
|
|
||||||
+ email String @unique
|
|
||||||
+ password String
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+model Task {
|
|
||||||
+ id Int @id @default(autoincrement())
|
|
||||||
+ description String
|
|
||||||
+ isDone Boolean @default(false)
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "sqlite"
|
|
||||||
url = "***"
|
|
||||||
}
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
output = "../server/node_modules/.prisma/client"
|
|
||||||
}
|
|
||||||
|
|
||||||
model User {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
email String @unique
|
|
||||||
password String
|
|
||||||
}
|
|
||||||
|
|
||||||
model Task {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
description String
|
|
||||||
isDone Boolean @default(false)
|
|
||||||
}
|
|
||||||
|
|
@ -1,185 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.3.14-fixed",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"tag": "CreateSource",
|
|
||||||
"source": "db"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Source",
|
|
||||||
"source": "db"
|
|
||||||
},
|
|
||||||
"argument": "provider",
|
|
||||||
"value": "\"sqlite\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Source",
|
|
||||||
"source": "db"
|
|
||||||
},
|
|
||||||
"argument": "url",
|
|
||||||
"value": "\"***\""
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateModel",
|
|
||||||
"model": "User"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "User",
|
|
||||||
"field": "id",
|
|
||||||
"type": "Int",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "User",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "id"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "User",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Directive",
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "User",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
},
|
|
||||||
"argument": "",
|
|
||||||
"value": "autoincrement()"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "User",
|
|
||||||
"field": "email",
|
|
||||||
"type": "String",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "User",
|
|
||||||
"field": "email"
|
|
||||||
},
|
|
||||||
"directive": "unique"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "User",
|
|
||||||
"field": "password",
|
|
||||||
"type": "String",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateModel",
|
|
||||||
"model": "Task"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "id",
|
|
||||||
"type": "Int",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "id"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Directive",
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "id"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
},
|
|
||||||
"argument": "",
|
|
||||||
"value": "autoincrement()"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "description",
|
|
||||||
"type": "String",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "isDone",
|
|
||||||
"type": "Boolean",
|
|
||||||
"arity": "Required"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "isDone"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Directive",
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "isDone"
|
|
||||||
},
|
|
||||||
"directive": "default"
|
|
||||||
},
|
|
||||||
"argument": "",
|
|
||||||
"value": "false"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
# Migration `20201023121536-b`
|
|
||||||
|
|
||||||
This migration has been generated by Martin Sosic at 10/23/2020, 2:15:36 PM.
|
|
||||||
You can check out the [state of the schema](./schema.prisma) after the migration.
|
|
||||||
|
|
||||||
## Database Steps
|
|
||||||
|
|
||||||
```sql
|
|
||||||
PRAGMA foreign_keys=OFF;
|
|
||||||
CREATE TABLE "new_Task" (
|
|
||||||
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
|
||||||
"description" TEXT NOT NULL,
|
|
||||||
"isDone" BOOLEAN NOT NULL DEFAULT false,
|
|
||||||
"userId" INTEGER,
|
|
||||||
|
|
||||||
FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
INSERT INTO "new_Task" ("id", "description", "isDone") SELECT "id", "description", "isDone" FROM "Task";
|
|
||||||
DROP TABLE "Task";
|
|
||||||
ALTER TABLE "new_Task" RENAME TO "Task";
|
|
||||||
PRAGMA foreign_key_check;
|
|
||||||
PRAGMA foreign_keys=ON
|
|
||||||
```
|
|
||||||
|
|
||||||
## Changes
|
|
||||||
|
|
||||||
```diff
|
|
||||||
diff --git schema.prisma schema.prisma
|
|
||||||
migration 20201023121126-a..20201023121536-b
|
|
||||||
--- datamodel.dml
|
|
||||||
+++ datamodel.dml
|
|
||||||
@@ -1,8 +1,8 @@
|
|
||||||
datasource db {
|
|
||||||
provider = "sqlite"
|
|
||||||
- url = "***"
|
|
||||||
+ url = "***"
|
|
||||||
}
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
@@ -12,12 +12,15 @@
|
|
||||||
model User {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
email String @unique
|
|
||||||
password String
|
|
||||||
+ tasks Task[]
|
|
||||||
}
|
|
||||||
model Task {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
description String
|
|
||||||
isDone Boolean @default(false)
|
|
||||||
+ user User? @relation(fields: [userId], references: [id])
|
|
||||||
+ userId Int?
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
|
|
||||||
datasource db {
|
|
||||||
provider = "sqlite"
|
|
||||||
url = "***"
|
|
||||||
}
|
|
||||||
|
|
||||||
generator client {
|
|
||||||
provider = "prisma-client-js"
|
|
||||||
output = "../server/node_modules/.prisma/client"
|
|
||||||
}
|
|
||||||
|
|
||||||
model User {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
email String @unique
|
|
||||||
password String
|
|
||||||
tasks Task[]
|
|
||||||
}
|
|
||||||
|
|
||||||
model Task {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
description String
|
|
||||||
isDone Boolean @default(false)
|
|
||||||
user User? @relation(fields: [userId], references: [id])
|
|
||||||
userId Int?
|
|
||||||
}
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.3.14-fixed",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "User",
|
|
||||||
"field": "tasks",
|
|
||||||
"type": "Task",
|
|
||||||
"arity": "List"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "user",
|
|
||||||
"type": "User",
|
|
||||||
"arity": "Optional"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateDirective",
|
|
||||||
"location": {
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "user"
|
|
||||||
},
|
|
||||||
"directive": "relation"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Directive",
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "user"
|
|
||||||
},
|
|
||||||
"directive": "relation"
|
|
||||||
},
|
|
||||||
"argument": "fields",
|
|
||||||
"value": "[userId]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateArgument",
|
|
||||||
"location": {
|
|
||||||
"tag": "Directive",
|
|
||||||
"path": {
|
|
||||||
"tag": "Field",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "user"
|
|
||||||
},
|
|
||||||
"directive": "relation"
|
|
||||||
},
|
|
||||||
"argument": "references",
|
|
||||||
"value": "[id]"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"tag": "CreateField",
|
|
||||||
"model": "Task",
|
|
||||||
"field": "userId",
|
|
||||||
"type": "Int",
|
|
||||||
"arity": "Optional"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -0,0 +1,18 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "User" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"email" TEXT NOT NULL,
|
||||||
|
"password" TEXT NOT NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE "Task" (
|
||||||
|
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
|
||||||
|
"description" TEXT NOT NULL,
|
||||||
|
"isDone" BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
"userId" INTEGER,
|
||||||
|
FOREIGN KEY ("userId") REFERENCES "User" ("id") ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
-- CreateIndex
|
||||||
|
CREATE UNIQUE INDEX "User.email_unique" ON "User"("email");
|
@ -1,4 +0,0 @@
|
|||||||
# Prisma Migrate lockfile v1
|
|
||||||
|
|
||||||
20201023121126-a
|
|
||||||
20201023121536-b
|
|
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "sqlite"
|
@ -136,12 +136,12 @@ CLI is actually `wasp` executable, and it uses the library, where most of the lo
|
|||||||
|
|
||||||
Wasp compiler takes .wasp files + everything in the `ext/` dir (JS, HTML, ...) and generates a web app that consists of client, server and database.
|
Wasp compiler takes .wasp files + everything in the `ext/` dir (JS, HTML, ...) and generates a web app that consists of client, server and database.
|
||||||
|
|
||||||
Wasp compiler code is split into 2 basic layers: Parser and Generator.
|
Wasp compiler code is split into 2 basic layers: Analyzer (frontend) and Generator (backend).
|
||||||
|
|
||||||
Wasp file(s) are parsed by Parser into an AST (Abstract Syntax Tree) described in `src/Wasp.hs`.
|
Wasp file(s) are analyzed by Analyzer, where they are first parsed, then typechecked, and then evaluated into a central IR (Intermediate Representation), which is `AppSpec` (`src/Wasp/AppSpec.hs`).
|
||||||
Parser is implemented via parser combinators and has no distinct tokenization, syntax and semantic analysis steps, currently it is all one big step.
|
Check `src/Wasp/Analyzer.hs` for more details.
|
||||||
|
|
||||||
AST generated by Parser is passed to the Generator, which based on it decides how to generate a web app.
|
AppSpec is passed to the Generator, which based on it decides how to generate a web app.
|
||||||
Output of Generator is a list of FileDrafts, where each FileDraft explains how to create a file on the disk.
|
Output of Generator is a list of FileDrafts, where each FileDraft explains how to create a file on the disk.
|
||||||
Therefore, Generator doesn't generate anything itself, instead it provides instructions (FileDrafts) on how to generate the web app.
|
Therefore, Generator doesn't generate anything itself, instead it provides instructions (FileDrafts) on how to generate the web app.
|
||||||
FileDrafts are using mustache templates a lot (they can be found in `data/Generator/templates`).
|
FileDrafts are using mustache templates a lot (they can be found in `data/Generator/templates`).
|
||||||
|
@ -106,8 +106,8 @@ createNewProject' (ProjectName projectName) = do
|
|||||||
" title: \"%s\"" `printf` projectName,
|
" title: \"%s\"" `printf` projectName,
|
||||||
"}",
|
"}",
|
||||||
"",
|
"",
|
||||||
"route \"/\" -> page Main",
|
"route RootRoute { path: \"/\", to: MainPage }",
|
||||||
"page Main {",
|
"page MainPage {",
|
||||||
" component: import Main from \"@ext/MainPage.js\"",
|
" component: import Main from \"@ext/MainPage.js\"",
|
||||||
"}"
|
"}"
|
||||||
]
|
]
|
||||||
|
@ -4,11 +4,12 @@ module Wasp.Cli.Command.Deps
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Control.Monad.IO.Class (liftIO)
|
import Control.Monad.IO.Class (liftIO)
|
||||||
|
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||||
import Wasp.Cli.Command (Command)
|
import Wasp.Cli.Command (Command)
|
||||||
import Wasp.Cli.Terminal (title)
|
import Wasp.Cli.Terminal (title)
|
||||||
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
||||||
import qualified Wasp.Generator.WebAppGenerator as WebAppGenerator
|
import qualified Wasp.Generator.WebAppGenerator as WebAppGenerator
|
||||||
import Wasp.NpmDependency (printDep)
|
import qualified Wasp.Util.Terminal as Term
|
||||||
|
|
||||||
deps :: Command ()
|
deps :: Command ()
|
||||||
deps =
|
deps =
|
||||||
@ -25,3 +26,9 @@ deps =
|
|||||||
title "Webapp dependencies:"
|
title "Webapp dependencies:"
|
||||||
]
|
]
|
||||||
++ map printDep WebAppGenerator.waspNpmDeps
|
++ map printDep WebAppGenerator.waspNpmDeps
|
||||||
|
|
||||||
|
printDep :: AS.Dependency.Dependency -> String
|
||||||
|
printDep dep =
|
||||||
|
Term.applyStyles [Term.Cyan] (AS.Dependency.name dep)
|
||||||
|
++ "@"
|
||||||
|
++ Term.applyStyles [Term.Yellow] (AS.Dependency.version dep)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Cli.Command.Info
|
module Wasp.Cli.Command.Info
|
||||||
( info,
|
( info,
|
||||||
)
|
)
|
||||||
@ -8,19 +10,18 @@ import Control.Monad.IO.Class (liftIO)
|
|||||||
import StrongPath (Abs, Dir, Path', fromAbsFile, fromRelFile, toFilePath)
|
import StrongPath (Abs, Dir, Path', fromAbsFile, fromRelFile, toFilePath)
|
||||||
import StrongPath.Operations
|
import StrongPath.Operations
|
||||||
import System.Directory (doesFileExist, getFileSize)
|
import System.Directory (doesFileExist, getFileSize)
|
||||||
|
import qualified Wasp.Analyzer as Analyzer
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
import Wasp.Cli.Command (Command)
|
import Wasp.Cli.Command (Command)
|
||||||
import Wasp.Cli.Command.Common
|
import Wasp.Cli.Command.Common (findWaspProjectRootDirFromCwd, waspSaysC)
|
||||||
( findWaspProjectRootDirFromCwd,
|
|
||||||
waspSaysC,
|
|
||||||
)
|
|
||||||
import qualified Wasp.Cli.Common as Cli.Common
|
import qualified Wasp.Cli.Common as Cli.Common
|
||||||
import Wasp.Cli.Terminal (title)
|
import Wasp.Cli.Terminal (title)
|
||||||
import Wasp.Common (WaspProjectDir)
|
import Wasp.Common (WaspProjectDir)
|
||||||
|
import Wasp.Error (showCompilerErrorForTerminal)
|
||||||
import Wasp.Lib (findWaspFile)
|
import Wasp.Lib (findWaspFile)
|
||||||
import qualified Wasp.Parser
|
|
||||||
import Wasp.Util.IO (listDirectoryDeep)
|
import Wasp.Util.IO (listDirectoryDeep)
|
||||||
import qualified Wasp.Util.Terminal as Term
|
import qualified Wasp.Util.Terminal as Term
|
||||||
import Wasp.Wasp (Wasp, appName, getApp)
|
|
||||||
|
|
||||||
info :: Command ()
|
info :: Command ()
|
||||||
info =
|
info =
|
||||||
@ -28,17 +29,17 @@ info =
|
|||||||
waspDir <- findWaspProjectRootDirFromCwd
|
waspDir <- findWaspProjectRootDirFromCwd
|
||||||
compileInfo <- liftIO $ readCompileInformation waspDir
|
compileInfo <- liftIO $ readCompileInformation waspDir
|
||||||
projectSize <- liftIO $ readDirectorySizeMB waspDir
|
projectSize <- liftIO $ readDirectorySizeMB waspDir
|
||||||
waspAstOrError <- liftIO $ parseWaspFile waspDir
|
declsOrError <- liftIO $ parseWaspFile waspDir
|
||||||
case waspAstOrError of
|
case declsOrError of
|
||||||
Left err -> waspSaysC err
|
Left err -> waspSaysC err
|
||||||
Right wasp -> do
|
Right decls -> do
|
||||||
waspSaysC $
|
waspSaysC $
|
||||||
unlines
|
unlines
|
||||||
[ "",
|
[ "",
|
||||||
title "Project information",
|
title "Project information",
|
||||||
printInfo
|
printInfo
|
||||||
"Name"
|
"Name"
|
||||||
(appName $ getApp wasp),
|
(fst $ head $ AS.takeDecls @AS.App.App decls),
|
||||||
printInfo
|
printInfo
|
||||||
"Last compile"
|
"Last compile"
|
||||||
compileInfo,
|
compileInfo,
|
||||||
@ -55,17 +56,28 @@ readDirectorySizeMB path = (++ " MB") . show . (`div` 1000000) . sum <$> (listDi
|
|||||||
|
|
||||||
readCompileInformation :: Path' Abs (Dir WaspProjectDir) -> IO String
|
readCompileInformation :: Path' Abs (Dir WaspProjectDir) -> IO String
|
||||||
readCompileInformation waspDir = do
|
readCompileInformation waspDir = do
|
||||||
let dotWaspInfoFile = fromAbsFile $ waspDir </> Cli.Common.dotWaspDirInWaspProjectDir </> Cli.Common.generatedCodeDirInDotWaspDir </> Cli.Common.dotWaspInfoFileInGeneratedCodeDir
|
let dotWaspInfoFile =
|
||||||
|
fromAbsFile $
|
||||||
|
waspDir </> Cli.Common.dotWaspDirInWaspProjectDir
|
||||||
|
</> Cli.Common.generatedCodeDirInDotWaspDir
|
||||||
|
</> Cli.Common.dotWaspInfoFileInGeneratedCodeDir
|
||||||
dotWaspInfoFileExists <- doesFileExist dotWaspInfoFile
|
dotWaspInfoFileExists <- doesFileExist dotWaspInfoFile
|
||||||
if dotWaspInfoFileExists
|
if dotWaspInfoFileExists
|
||||||
then do readFile dotWaspInfoFile
|
then do readFile dotWaspInfoFile
|
||||||
else return "No compile information found"
|
else return "No compile information found"
|
||||||
|
|
||||||
parseWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String Wasp)
|
parseWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Either String [AS.Decl])
|
||||||
parseWaspFile waspDir = do
|
parseWaspFile waspDir = do
|
||||||
maybeWaspFile <- findWaspFile waspDir
|
maybeWaspFile <- findWaspFile waspDir
|
||||||
case maybeWaspFile of
|
case maybeWaspFile of
|
||||||
Nothing -> return (Left "Couldn't find a single *.wasp file.")
|
Nothing -> return (Left "Couldn't find a single *.wasp file.")
|
||||||
Just waspFile -> do
|
Just waspFile ->
|
||||||
|
do
|
||||||
waspStr <- readFile (toFilePath waspFile)
|
waspStr <- readFile (toFilePath waspFile)
|
||||||
return $ left (("Couldn't parse .wasp file: " <>) . show) $ Wasp.Parser.parseWasp waspStr
|
return $
|
||||||
|
left
|
||||||
|
( ("Couldn't parse .wasp file:\n" <>)
|
||||||
|
. showCompilerErrorForTerminal (waspFile, waspStr)
|
||||||
|
. Analyzer.getErrorMessageAndCtx
|
||||||
|
)
|
||||||
|
$ Analyzer.analyze waspStr
|
||||||
|
@ -12,7 +12,7 @@ const MainPage = () => {
|
|||||||
|
|
||||||
<h2 className="welcome-title"> Welcome to Wasp - you just started a new app! </h2>
|
<h2 className="welcome-title"> Welcome to Wasp - you just started a new app! </h2>
|
||||||
<h3 className="welcome-subtitle">
|
<h3 className="welcome-subtitle">
|
||||||
This is page <code>Main</code> located at route <code>/</code>.
|
This is page <code>MainPage</code> located at route <code>/</code>.
|
||||||
Open <code>ext/MainPage.js</code> to edit it.
|
Open <code>ext/MainPage.js</code> to edit it.
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={{> <}}=}}
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||||
|
|
||||||
## Available Scripts
|
## Available Scripts
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={{> <}}=}}
|
|
||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||||
|
|
||||||
# dependencies
|
# dependencies
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{={= =}=}}
|
{{={= =}=}}
|
||||||
{
|
{
|
||||||
"name": "{= wasp.app.name =}",
|
"name": "{= appName =}",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
{=& depsChunk =},
|
{=& depsChunk =},
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{{={{> <}}=}}
|
{{={= =}=}}
|
||||||
{
|
{
|
||||||
"name": "{{> app.name <}}",
|
"name": "{= appName =}",
|
||||||
"icons": [
|
"icons": [
|
||||||
{
|
{
|
||||||
"src": "favicon.ico",
|
"src": "favicon.ico",
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={{> <}}=}}
|
|
||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={= =}=}}
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import { ReactQueryCacheProvider } from 'react-query'
|
import { ReactQueryCacheProvider } from 'react-query'
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={= =}=}}
|
|
||||||
import api, { handleApiError } from '../api.js'
|
import api, { handleApiError } from '../api.js'
|
||||||
import config from '../config.js'
|
import config from '../config.js'
|
||||||
|
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
{{={{> <}}=}}
|
|
||||||
// This optional code is used to register a service worker.
|
// This optional code is used to register a service worker.
|
||||||
// register() is not called by default.
|
// register() is not called by default.
|
||||||
|
|
||||||
|
@ -2,18 +2,19 @@ app todoApp {
|
|||||||
title: "ToDo App",
|
title: "ToDo App",
|
||||||
head: [
|
head: [
|
||||||
"<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap\" />"
|
"<link rel=\"stylesheet\" href=\"https://fonts.googleapis.com/css?family=Roboto:300,400,500&display=swap\" />"
|
||||||
]
|
],
|
||||||
}
|
dependencies: [
|
||||||
|
("@material-ui/core", "4.11.3")
|
||||||
dependencies {=json
|
],
|
||||||
"@material-ui/core": "4.11.3"
|
auth: {
|
||||||
json=}
|
|
||||||
|
|
||||||
auth {
|
|
||||||
userEntity: User,
|
userEntity: User,
|
||||||
methods: [ EmailAndPassword ],
|
methods: [ EmailAndPassword ],
|
||||||
onAuthFailedRedirectTo: "/login",
|
onAuthFailedRedirectTo: "/login",
|
||||||
onAuthSucceededRedirectTo: "/profile"
|
onAuthSucceededRedirectTo: "/profile"
|
||||||
|
},
|
||||||
|
server: {
|
||||||
|
setupFn: import setup from "@ext/serverSetup.js"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
entity User {=psl
|
entity User {=psl
|
||||||
@ -31,41 +32,38 @@ entity Task {=psl
|
|||||||
userId Int
|
userId Int
|
||||||
psl=}
|
psl=}
|
||||||
|
|
||||||
server {
|
|
||||||
setupFn: import setup from "@ext/serverSetup.js"
|
|
||||||
}
|
|
||||||
|
|
||||||
route "/signup" -> page Signup
|
route SignupRoute { path: "/signup", to: SignupPage }
|
||||||
page Signup {
|
page SignupPage {
|
||||||
component: import Signup from "@ext/pages/auth/Signup"
|
component: import Signup from "@ext/pages/auth/Signup"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/login" -> page Login
|
route LoginRoute { path: "/login", to: LoginPage }
|
||||||
page Login {
|
page LoginPage {
|
||||||
component: import Login from "@ext/pages/auth/Login"
|
component: import Login from "@ext/pages/auth/Login"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/" -> page Main
|
route HomeRoute { path: "/", to: MainPage }
|
||||||
page Main {
|
page MainPage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import Main from "@ext/pages/Main"
|
component: import Main from "@ext/pages/Main"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/about" -> page About
|
route AboutRoute { path: "/about", to: AboutPage }
|
||||||
page About {
|
page AboutPage {
|
||||||
component: import About from "@ext/pages/About"
|
component: import About from "@ext/pages/About"
|
||||||
}
|
}
|
||||||
|
|
||||||
route "/profile" -> page Profile
|
route ProfileRoute { path: "/profile", to: ProfilePage }
|
||||||
page Profile {
|
page ProfilePage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import { ProfilePage } from "@ext/pages/ProfilePage"
|
component: import { ProfilePage } from "@ext/pages/ProfilePage"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Page for viewing a specific task
|
// Page for viewing a specific task
|
||||||
//
|
//
|
||||||
route "/task/:id" -> page Task
|
route TaskRoute { path: "/task/:id", to: TaskPage }
|
||||||
page Task {
|
page TaskPage {
|
||||||
authRequired: true,
|
authRequired: true,
|
||||||
component: import Task from "@ext/pages/Task"
|
component: import Task from "@ext/pages/Task"
|
||||||
}
|
}
|
||||||
@ -79,7 +77,7 @@ query getTasks {
|
|||||||
|
|
||||||
query getNumTasks {
|
query getNumTasks {
|
||||||
fn: import { getNumTasks } from "@ext/queries.js",
|
fn: import { getNumTasks } from "@ext/queries.js",
|
||||||
entities: [Task],
|
|
||||||
auth: false
|
auth: false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Binary file not shown.
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 59 KiB |
@ -76,7 +76,7 @@ library:
|
|||||||
- unliftio
|
- unliftio
|
||||||
- array # Used by code generated by Alex for src/Analyzer/Parser/Lexer.x
|
- array # Used by code generated by Alex for src/Analyzer/Parser/Lexer.x
|
||||||
- mtl
|
- mtl
|
||||||
- strong-path
|
- strong-path >= 1.1.2.0
|
||||||
- template-haskell
|
- template-haskell
|
||||||
- path-io
|
- path-io
|
||||||
|
|
||||||
|
@ -19,6 +19,8 @@ module Wasp.Analyzer.Evaluator.Evaluation.TypedExpr.Combinators
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Control.Arrow (left)
|
import Control.Arrow (left)
|
||||||
|
import Data.List (stripPrefix)
|
||||||
|
import qualified StrongPath as SP
|
||||||
import Wasp.Analyzer.Evaluator.Evaluation.Internal (evaluation, evaluation', runEvaluation)
|
import Wasp.Analyzer.Evaluator.Evaluation.Internal (evaluation, evaluation', runEvaluation)
|
||||||
import Wasp.Analyzer.Evaluator.Evaluation.TypedExpr (TypedExprEvaluation)
|
import Wasp.Analyzer.Evaluator.Evaluation.TypedExpr (TypedExprEvaluation)
|
||||||
import qualified Wasp.Analyzer.Evaluator.EvaluationError as ER
|
import qualified Wasp.Analyzer.Evaluator.EvaluationError as ER
|
||||||
@ -152,8 +154,24 @@ tuple4 eval1 eval2 eval3 eval4 = evaluation $ \(typeDefs, bindings) -> withCtx $
|
|||||||
-- | An evaluation that expects an "ExtImport".
|
-- | An evaluation that expects an "ExtImport".
|
||||||
extImport :: TypedExprEvaluation AppSpec.ExtImport.ExtImport
|
extImport :: TypedExprEvaluation AppSpec.ExtImport.ExtImport
|
||||||
extImport = evaluation' . withCtx $ \ctx -> \case
|
extImport = evaluation' . withCtx $ \ctx -> \case
|
||||||
TypedAST.ExtImport name file -> pure $ AppSpec.ExtImport.ExtImport name file
|
TypedAST.ExtImport name extFileFP ->
|
||||||
|
-- NOTE(martin): This parsing here could instead be done in Parser.
|
||||||
|
-- I don't have a very good reason for doing it here instead of Parser, except
|
||||||
|
-- for being somewhat simpler to implement.
|
||||||
|
-- So we might want to move it to Parser at some point in the future, if we
|
||||||
|
-- figure out that is better (it sounds/feels like it could be).
|
||||||
|
case stripPrefix extPrefix extFileFP of
|
||||||
|
Just relFileFP -> case SP.parseRelFileP relFileFP of
|
||||||
|
Left err -> Left $ ER.mkEvaluationError ctx $ ER.ParseError $ ER.EvaluationParseError $ show err
|
||||||
|
Right relFileSP -> pure $ AppSpec.ExtImport.ExtImport name relFileSP
|
||||||
|
Nothing ->
|
||||||
|
Left $
|
||||||
|
ER.mkEvaluationError ctx $
|
||||||
|
ER.ParseError $
|
||||||
|
ER.EvaluationParseError $ "Path in external import must start with \"" ++ extPrefix ++ "\"!"
|
||||||
expr -> Left $ ER.mkEvaluationError ctx $ ER.ExpectedType T.ExtImportType (TypedAST.exprType expr)
|
expr -> Left $ ER.mkEvaluationError ctx $ ER.ExpectedType T.ExtImportType (TypedAST.exprType expr)
|
||||||
|
where
|
||||||
|
extPrefix = "@ext/"
|
||||||
|
|
||||||
-- | An evaluation that expects a "JSON".
|
-- | An evaluation that expects a "JSON".
|
||||||
json :: TypedExprEvaluation AppSpec.JSON.JSON
|
json :: TypedExprEvaluation AppSpec.JSON.JSON
|
||||||
|
@ -29,12 +29,16 @@ $any = [.$white]
|
|||||||
@double = "-"? $digit+ "." $digit+
|
@double = "-"? $digit+ "." $digit+
|
||||||
@integer = "-"? $digit+
|
@integer = "-"? $digit+
|
||||||
@ident = $identstart $ident* "'"*
|
@ident = $identstart $ident* "'"*
|
||||||
|
@linecomment = "//" [^\n\r]*
|
||||||
|
@blockcomment = "/*" (("*"[^\/]) | [^\*] | $white)* "*/" -- Based on https://stackoverflow.com/a/16165598/1509394 .
|
||||||
|
|
||||||
-- Tokenization rules (regex -> token)
|
-- Tokenization rules (regex -> token)
|
||||||
tokens :-
|
tokens :-
|
||||||
|
|
||||||
-- Skips whitespace
|
-- Skips whitespace and comments
|
||||||
<0> $white+ ;
|
<0> $white+ ;
|
||||||
|
<0> @linecomment ;
|
||||||
|
<0> @blockcomment ;
|
||||||
|
|
||||||
-- Quoter rules:
|
-- Quoter rules:
|
||||||
-- Uses Alex start codes to lex quoted characters with different rules:
|
-- Uses Alex start codes to lex quoted characters with different rules:
|
||||||
|
@ -7,7 +7,7 @@ module Wasp.Analyzer.Parser.ParseError
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Wasp.Analyzer.Parser.Ctx (Ctx, WithCtx (..), ctxFromPos, ctxFromRgn, getCtxRgn)
|
import Wasp.Analyzer.Parser.Ctx (Ctx, WithCtx (..), ctxFromPos, ctxFromRgn, getCtxRgn)
|
||||||
import Wasp.Analyzer.Parser.SourcePosition (SourcePosition)
|
import Wasp.Analyzer.Parser.SourcePosition (SourcePosition (..))
|
||||||
import Wasp.Analyzer.Parser.SourceRegion (getRgnEnd, getRgnStart)
|
import Wasp.Analyzer.Parser.SourceRegion (getRgnEnd, getRgnStart)
|
||||||
import Wasp.Analyzer.Parser.Token (Token (..))
|
import Wasp.Analyzer.Parser.Token (Token (..))
|
||||||
|
|
||||||
@ -40,7 +40,9 @@ getErrorMessageAndCtx = \case
|
|||||||
"Expected one of the following tokens instead: "
|
"Expected one of the following tokens instead: "
|
||||||
++ unwords expectedTokens
|
++ unwords expectedTokens
|
||||||
in unexpectedTokenMessage ++ if not (null expectedTokens) then "\n" ++ expectedTokensMessage else "",
|
in unexpectedTokenMessage ++ if not (null expectedTokens) then "\n" ++ expectedTokensMessage else "",
|
||||||
ctxFromPos $ tokenStartPosition unexpectedToken
|
let tokenStartPos@(SourcePosition sl sc) = tokenStartPosition unexpectedToken
|
||||||
|
tokenEndPos = SourcePosition sl (sc + length (tokenLexeme unexpectedToken) - 1)
|
||||||
|
in ctxFromRgn tokenStartPos tokenEndPos
|
||||||
)
|
)
|
||||||
QuoterDifferentTags (WithCtx lctx ltag) (WithCtx rctx rtag) ->
|
QuoterDifferentTags (WithCtx lctx ltag) (WithCtx rctx rtag) ->
|
||||||
let ctx = ctxFromRgn (getRgnStart $ getCtxRgn lctx) (getRgnEnd $ getCtxRgn rctx)
|
let ctx = ctxFromRgn (getRgnStart $ getCtxRgn lctx) (getRgnEnd $ getCtxRgn rctx)
|
||||||
|
@ -275,12 +275,11 @@ waspKindOfHaskellType typ = do
|
|||||||
maybeCustomEvaluationKind <- tryCastingToCustomEvaluationKind typ
|
maybeCustomEvaluationKind <- tryCastingToCustomEvaluationKind typ
|
||||||
maybeRecordKind <- tryCastingToRecordKind typ
|
maybeRecordKind <- tryCastingToRecordKind typ
|
||||||
maybe (fail $ "No translation to wasp type for type " ++ show typ) return $
|
maybe (fail $ "No translation to wasp type for type " ++ show typ) return $
|
||||||
maybeDeclRefKind
|
-- NOTE: It is important to have @maybeCustomEvaluationKind@ first, since we want it to override
|
||||||
|
-- any of the hardcoded kind assignments below.
|
||||||
|
maybeCustomEvaluationKind
|
||||||
|
<|> maybeDeclRefKind
|
||||||
<|> maybeEnumKind
|
<|> maybeEnumKind
|
||||||
-- NOTE: It is important that @maybeCustomEvaluationKind@ is before @maybeRecordKind@,
|
|
||||||
-- since having a custom evaluation should override typical record evalution, if type is a record.
|
|
||||||
<|> maybeCustomEvaluationKind
|
|
||||||
<|> maybeRecordKind
|
|
||||||
<|> case typ of
|
<|> case typ of
|
||||||
ConT name
|
ConT name
|
||||||
| name == ''String -> pure KString
|
| name == ''String -> pure KString
|
||||||
@ -295,6 +294,10 @@ waspKindOfHaskellType typ = do
|
|||||||
TupleT 3 `AppT` t1 `AppT` t2 `AppT` t3 -> pure (KTuple (t1, t2, [t3]))
|
TupleT 3 `AppT` t1 `AppT` t2 `AppT` t3 -> pure (KTuple (t1, t2, [t3]))
|
||||||
TupleT 4 `AppT` t1 `AppT` t2 `AppT` t3 `AppT` t4 -> pure (KTuple (t1, t2, [t3, t4]))
|
TupleT 4 `AppT` t1 `AppT` t2 `AppT` t3 `AppT` t4 -> pure (KTuple (t1, t2, [t3, t4]))
|
||||||
_ -> Nothing
|
_ -> Nothing
|
||||||
|
-- NOTE: It is important that @maybeRecordKind@ is last, in case there are some other custom/specialized
|
||||||
|
-- kind assignments for a specific record type -> in that case we want those to be used,
|
||||||
|
-- instead of a generic record kind assignment.
|
||||||
|
<|> maybeRecordKind
|
||||||
where
|
where
|
||||||
tryCastingToDeclRefKind :: Type -> Q (Maybe WaspKind)
|
tryCastingToDeclRefKind :: Type -> Q (Maybe WaspKind)
|
||||||
tryCastingToDeclRefKind (ConT name `AppT` subType) | name == ''Ref = do
|
tryCastingToDeclRefKind (ConT name `AppT` subType) | name == ''Ref = do
|
||||||
|
@ -1,11 +1,34 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.AppSpec
|
module Wasp.AppSpec
|
||||||
( AppSpec (..),
|
( AppSpec (..),
|
||||||
|
Decl,
|
||||||
|
getDecls,
|
||||||
|
takeDecls,
|
||||||
|
Ref,
|
||||||
|
refName,
|
||||||
|
getApp,
|
||||||
|
getActions,
|
||||||
|
getQueries,
|
||||||
|
getEntities,
|
||||||
|
getPages,
|
||||||
|
getRoutes,
|
||||||
|
isAuthEnabled,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Data.Maybe (isJust)
|
||||||
import StrongPath (Abs, Dir, File', Path')
|
import StrongPath (Abs, Dir, File', Path')
|
||||||
import Wasp.AppSpec.Core.Decl (Decl)
|
import Wasp.AppSpec.Action (Action)
|
||||||
|
import Wasp.AppSpec.App (App)
|
||||||
|
import qualified Wasp.AppSpec.App as App
|
||||||
|
import Wasp.AppSpec.Core.Decl (Decl, IsDecl, takeDecls)
|
||||||
|
import Wasp.AppSpec.Core.Ref (Ref, refName)
|
||||||
|
import Wasp.AppSpec.Entity (Entity)
|
||||||
import qualified Wasp.AppSpec.ExternalCode as ExternalCode
|
import qualified Wasp.AppSpec.ExternalCode as ExternalCode
|
||||||
|
import Wasp.AppSpec.Page (Page)
|
||||||
|
import Wasp.AppSpec.Query (Query)
|
||||||
|
import Wasp.AppSpec.Route (Route)
|
||||||
import Wasp.Common (DbMigrationsDir)
|
import Wasp.Common (DbMigrationsDir)
|
||||||
|
|
||||||
-- | AppSpec is the main/central intermediate representation (IR) of the whole Wasp compiler,
|
-- | AppSpec is the main/central intermediate representation (IR) of the whole Wasp compiler,
|
||||||
@ -21,6 +44,46 @@ data AppSpec = AppSpec
|
|||||||
externalCodeDirPath :: !(Path' Abs (Dir ExternalCode.SourceExternalCodeDir)),
|
externalCodeDirPath :: !(Path' Abs (Dir ExternalCode.SourceExternalCodeDir)),
|
||||||
-- | Absolute path to the directory in wasp project source that contains database migrations.
|
-- | Absolute path to the directory in wasp project source that contains database migrations.
|
||||||
migrationsDir :: Maybe (Path' Abs (Dir DbMigrationsDir)),
|
migrationsDir :: Maybe (Path' Abs (Dir DbMigrationsDir)),
|
||||||
|
-- | Absolute path to the .env file in wasp project source. It contains env variables to be
|
||||||
|
-- provided to the server only during the development.
|
||||||
dotEnvFile :: Maybe (Path' Abs File'),
|
dotEnvFile :: Maybe (Path' Abs File'),
|
||||||
|
-- | If true, it means project is being compiled for production/deployment -> it is being "built".
|
||||||
|
-- If false, it means project is being compiled for development purposes (e.g. "wasp start").
|
||||||
isBuild :: Bool
|
isBuild :: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
-- TODO: Make this return "Named" declarations?
|
||||||
|
-- We would have something like NamedDecl or smth like that. Or at least have a @type Named@ or smth like that.
|
||||||
|
-- Or @WithName@ or just @Named@.
|
||||||
|
-- I like the best: `newtype Named a = Named (String, a)`
|
||||||
|
-- I created a github issue for it: https://github.com/wasp-lang/wasp/issues/426 .
|
||||||
|
getDecls :: IsDecl a => AppSpec -> [(String, a)]
|
||||||
|
getDecls = takeDecls . decls
|
||||||
|
|
||||||
|
-- TODO: This will fail with an error if there is no `app` declaration (because of `head`)!
|
||||||
|
-- However, returning a Maybe here would be PITA later in the code.
|
||||||
|
-- It would be cool instead if we had an extra step that somehow ensures that app exists and
|
||||||
|
-- throws nice error if it doesn't. Some step that validated AppSpec. Maybe we could
|
||||||
|
-- have a function that returns `Validated AppSpec` -> so basically smart constructor,
|
||||||
|
-- validates AppSpec and returns it wrapped with `Validated`,
|
||||||
|
-- I created a github issue for it: https://github.com/wasp-lang/wasp/issues/425 .
|
||||||
|
getApp :: AppSpec -> (String, App)
|
||||||
|
getApp spec = head $ takeDecls @App (decls spec)
|
||||||
|
|
||||||
|
getQueries :: AppSpec -> [(String, Query)]
|
||||||
|
getQueries spec = takeDecls @Query (decls spec)
|
||||||
|
|
||||||
|
getActions :: AppSpec -> [(String, Action)]
|
||||||
|
getActions spec = takeDecls @Action (decls spec)
|
||||||
|
|
||||||
|
getEntities :: AppSpec -> [(String, Entity)]
|
||||||
|
getEntities spec = takeDecls @Entity (decls spec)
|
||||||
|
|
||||||
|
getPages :: AppSpec -> [(String, Page)]
|
||||||
|
getPages spec = takeDecls @Page (decls spec)
|
||||||
|
|
||||||
|
getRoutes :: AppSpec -> [(String, Route)]
|
||||||
|
getRoutes spec = takeDecls @Route (decls spec)
|
||||||
|
|
||||||
|
isAuthEnabled :: AppSpec -> Bool
|
||||||
|
isAuthEnabled spec = isJust (App.auth $ snd $ getApp spec)
|
||||||
|
@ -12,12 +12,9 @@ import Wasp.AppSpec.Core.Decl (IsDecl)
|
|||||||
data App = App
|
data App = App
|
||||||
{ title :: String,
|
{ title :: String,
|
||||||
head :: Maybe [String],
|
head :: Maybe [String],
|
||||||
auth :: Maybe Auth, -- NOTE: This is new. Before, `auth` was a standalone declaration.
|
auth :: Maybe Auth,
|
||||||
server :: Maybe Server, -- NOTE: This is new. Before, `server` was a standalone declaration.
|
server :: Maybe Server,
|
||||||
db :: Maybe Db, -- NOTE: This is new. Before, `db` was a standalone declaration.
|
db :: Maybe Db,
|
||||||
|
|
||||||
-- | NOTE: This is new. Before, `dependencies` was a standalone declaration and it was a {=json json=},
|
|
||||||
-- while now it is a [{ name :: String, version :: String }].
|
|
||||||
dependencies :: Maybe [Dependency]
|
dependencies :: Maybe [Dependency]
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Data)
|
deriving (Show, Eq, Data)
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
module Wasp.AppSpec.App.Dependency
|
module Wasp.AppSpec.App.Dependency
|
||||||
( Dependency (..),
|
( Dependency (..),
|
||||||
|
fromList,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -12,3 +13,6 @@ data Dependency = Dependency
|
|||||||
version :: String
|
version :: String
|
||||||
}
|
}
|
||||||
deriving (Show, Eq, Data)
|
deriving (Show, Eq, Data)
|
||||||
|
|
||||||
|
fromList :: [(String, String)] -> [Dependency]
|
||||||
|
fromList = map (\(n, v) -> Dependency {name = n, version = v})
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
module Wasp.AppSpec.Core.Ref
|
module Wasp.AppSpec.Core.Ref
|
||||||
( Ref (..),
|
( Ref (..),
|
||||||
|
refName,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -21,3 +22,6 @@ deriving instance Eq a => Eq (Ref a)
|
|||||||
deriving instance Show a => Show (Ref a)
|
deriving instance Show a => Show (Ref a)
|
||||||
|
|
||||||
deriving instance (IsDecl a, Data a) => Data (Ref a)
|
deriving instance (IsDecl a, Data a) => Data (Ref a)
|
||||||
|
|
||||||
|
refName :: Ref a -> String
|
||||||
|
refName (Ref name) = name
|
||||||
|
@ -7,11 +7,18 @@ module Wasp.AppSpec.ExtImport
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Data.Data (Data)
|
import Data.Data (Data)
|
||||||
|
import StrongPath (File', Path, Posix, Rel)
|
||||||
|
import Wasp.AppSpec.ExternalCode (SourceExternalCodeDir)
|
||||||
|
|
||||||
data ExtImport = ExtImport ExtImportName ExtImportPath
|
data ExtImport = ExtImport
|
||||||
|
{ -- | What is being imported.
|
||||||
|
name :: ExtImportName,
|
||||||
|
-- | Path from which we are importing.
|
||||||
|
path :: ExtImportPath
|
||||||
|
}
|
||||||
deriving (Show, Eq, Data)
|
deriving (Show, Eq, Data)
|
||||||
|
|
||||||
type ExtImportPath = String
|
type ExtImportPath = Path Posix (Rel SourceExternalCodeDir) File'
|
||||||
|
|
||||||
type Identifier = String
|
type Identifier = String
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE DeriveDataTypeable #-}
|
||||||
|
|
||||||
module Wasp.AppSpec.ExternalCode
|
module Wasp.AppSpec.ExternalCode
|
||||||
( -- | Wasp project consists of Wasp code (.wasp files) and external code (e.g. .js files) that is
|
( -- | Wasp project consists of Wasp code (.wasp files) and external code (e.g. .js files) that is
|
||||||
-- used/referenced by the Wasp code.
|
-- used/referenced by the Wasp code.
|
||||||
@ -14,13 +16,14 @@ module Wasp.AppSpec.ExternalCode
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
import Data.Data (Data)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
import qualified Data.Text.Lazy as TextL
|
import qualified Data.Text.Lazy as TextL
|
||||||
import StrongPath (Abs, Dir, File', Path', Rel, (</>))
|
import StrongPath (Abs, Dir, File', Path', Rel, (</>))
|
||||||
|
|
||||||
-- | Directory in Wasp source that contains external code.
|
-- | Directory in Wasp source that contains external code.
|
||||||
-- External code files are obtained from it.
|
-- External code files are obtained from it.
|
||||||
data SourceExternalCodeDir
|
data SourceExternalCodeDir deriving (Data)
|
||||||
|
|
||||||
data File = File
|
data File = File
|
||||||
{ _pathInExtCodeDir :: !(Path' (Rel SourceExternalCodeDir) File'),
|
{ _pathInExtCodeDir :: !(Path' (Rel SourceExternalCodeDir) File'),
|
||||||
|
38
waspc/src/Wasp/AppSpec/Operation.hs
Normal file
38
waspc/src/Wasp/AppSpec/Operation.hs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
module Wasp.AppSpec.Operation
|
||||||
|
( Operation (..),
|
||||||
|
getName,
|
||||||
|
getFn,
|
||||||
|
getEntities,
|
||||||
|
getAuth,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
|
||||||
|
import Wasp.AppSpec.Action (Action)
|
||||||
|
import qualified Wasp.AppSpec.Action as Action
|
||||||
|
import Wasp.AppSpec.Core.Ref (Ref)
|
||||||
|
import Wasp.AppSpec.Entity (Entity)
|
||||||
|
import Wasp.AppSpec.ExtImport (ExtImport)
|
||||||
|
import Wasp.AppSpec.Query (Query)
|
||||||
|
import qualified Wasp.AppSpec.Query as Query
|
||||||
|
|
||||||
|
-- | Common "interface" for queries and actions.
|
||||||
|
data Operation
|
||||||
|
= QueryOp String Query
|
||||||
|
| ActionOp String Action
|
||||||
|
deriving (Show)
|
||||||
|
|
||||||
|
getName :: Operation -> String
|
||||||
|
getName (QueryOp name _) = name
|
||||||
|
getName (ActionOp name _) = name
|
||||||
|
|
||||||
|
getFn :: Operation -> ExtImport
|
||||||
|
getFn (QueryOp _ query) = Query.fn query
|
||||||
|
getFn (ActionOp _ action) = Action.fn action
|
||||||
|
|
||||||
|
getEntities :: Operation -> Maybe [Ref Entity]
|
||||||
|
getEntities (QueryOp _ query) = Query.entities query
|
||||||
|
getEntities (ActionOp _ action) = Action.entities action
|
||||||
|
|
||||||
|
getAuth :: Operation -> Maybe Bool
|
||||||
|
getAuth (QueryOp _ query) = Query.auth query
|
||||||
|
getAuth (ActionOp _ action) = Action.auth action
|
@ -10,7 +10,6 @@ import Wasp.AppSpec.Core.Decl (IsDecl)
|
|||||||
import Wasp.AppSpec.Core.Ref (Ref)
|
import Wasp.AppSpec.Core.Ref (Ref)
|
||||||
import Wasp.AppSpec.Page
|
import Wasp.AppSpec.Page
|
||||||
|
|
||||||
-- | NOTE: We have new syntax for route, before it was `route "/task" -> page Task`, now it is a dictionary.
|
|
||||||
data Route = Route
|
data Route = Route
|
||||||
{ path :: String,
|
{ path :: String,
|
||||||
-- TODO: In the future we might want to add other types of targets, for example another Route.
|
-- TODO: In the future we might want to add other types of targets, for example another Route.
|
||||||
|
76
waspc/src/Wasp/Error.hs
Normal file
76
waspc/src/Wasp/Error.hs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
module Wasp.Error (showCompilerErrorForTerminal) where
|
||||||
|
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import StrongPath (Abs, File', Path')
|
||||||
|
import qualified StrongPath as SP
|
||||||
|
import Wasp.Analyzer.Parser.Ctx (Ctx, getCtxRgn)
|
||||||
|
import Wasp.Analyzer.Parser.SourcePosition (SourcePosition (..))
|
||||||
|
import Wasp.Analyzer.Parser.SourceRegion (SourceRegion (..))
|
||||||
|
import Wasp.Util (indent, insertAt, leftPad)
|
||||||
|
import qualified Wasp.Util.Terminal as T
|
||||||
|
|
||||||
|
-- | Transforms compiler error (error with parse context) into an informative, pretty String that
|
||||||
|
-- can be printed directly into the terminal. It uses terminal features like escape codes
|
||||||
|
-- (colors, styling, ...).
|
||||||
|
showCompilerErrorForTerminal :: (Path' Abs File', String) -> (String, Ctx) -> String
|
||||||
|
showCompilerErrorForTerminal (waspFilePath, waspFileContent) (errMsg, errCtx) =
|
||||||
|
let srcRegion = getCtxRgn errCtx
|
||||||
|
in intercalate
|
||||||
|
"\n"
|
||||||
|
[ SP.fromAbsFile waspFilePath ++ " @ " ++ showRgn srcRegion,
|
||||||
|
indent 2 errMsg,
|
||||||
|
"",
|
||||||
|
indent 2 $ prettyShowSrcLinesOfErrorRgn waspFileContent srcRegion
|
||||||
|
]
|
||||||
|
|
||||||
|
showRgn :: SourceRegion -> String
|
||||||
|
showRgn (SourceRegion (SourcePosition l1 c1) (SourcePosition l2 c2))
|
||||||
|
| l1 == l2 && c1 == c2 = showPos l1 c1
|
||||||
|
| l1 == l2 && c1 /= c2 = show l1 ++ ":" ++ show c1 ++ "-" ++ show c2
|
||||||
|
| otherwise = showPos l1 c1 ++ " - " ++ showPos l2 c2
|
||||||
|
where
|
||||||
|
showPos l c = show l ++ ":" ++ show c
|
||||||
|
|
||||||
|
-- | Given wasp source and error region in it, extracts source lines
|
||||||
|
-- that are in the given error region and then nicely displays them,
|
||||||
|
-- by coloring in red the exact error region part of the code and also
|
||||||
|
-- by prefixing all the lines with their line number (colored yellow).
|
||||||
|
-- Uses terminal features for styling, like escape codes and similar.
|
||||||
|
prettyShowSrcLinesOfErrorRgn :: String -> SourceRegion -> String
|
||||||
|
prettyShowSrcLinesOfErrorRgn
|
||||||
|
waspFileContent
|
||||||
|
( SourceRegion
|
||||||
|
(SourcePosition startLineNum startColNum)
|
||||||
|
(SourcePosition endLineNum endColNum)
|
||||||
|
) =
|
||||||
|
let srcLines =
|
||||||
|
zip [max 1 (startLineNum - numCtxLines) ..] $
|
||||||
|
take (endLineNum - startLineNum + 1 + numCtxLines * 2) $
|
||||||
|
drop (startLineNum - 1 - numCtxLines) $
|
||||||
|
lines waspFileContent
|
||||||
|
srcLinesWithMarkedErrorRgn =
|
||||||
|
map
|
||||||
|
( \(lineNum, line) ->
|
||||||
|
let lineContainsError = lineNum >= startLineNum && lineNum <= endLineNum
|
||||||
|
lineWithStylingStartAndEnd =
|
||||||
|
if lineNum == startLineNum
|
||||||
|
then insertAt stylingStart (startColNum - 1) lineWithStylingEnd
|
||||||
|
else stylingStart ++ lineWithStylingEnd
|
||||||
|
lineWithStylingEnd =
|
||||||
|
if lineNum == endLineNum
|
||||||
|
then insertAt stylingEnd endColNum line
|
||||||
|
else line ++ stylingEnd
|
||||||
|
stylingStart = T.escapeCode ++ T.styleCode T.Red
|
||||||
|
stylingEnd = T.escapeCode ++ T.resetCode
|
||||||
|
in (lineNum, if lineContainsError then lineWithStylingStartAndEnd else line)
|
||||||
|
)
|
||||||
|
srcLines
|
||||||
|
srcLinesWithMarkedErrorRgnAndLineNumber =
|
||||||
|
map
|
||||||
|
(\(lineNum, line) -> T.applyStyles [T.Yellow] (leftPad ' ' 6 (show lineNum) ++ " | ") ++ line)
|
||||||
|
srcLinesWithMarkedErrorRgn
|
||||||
|
in intercalate "\n" srcLinesWithMarkedErrorRgnAndLineNumber
|
||||||
|
where
|
||||||
|
-- Number of lines to show before and after the source region containing error.
|
||||||
|
numCtxLines :: Int
|
||||||
|
numCtxLines = 1
|
@ -12,7 +12,7 @@ import qualified Data.Version
|
|||||||
import qualified Paths_waspc
|
import qualified Paths_waspc
|
||||||
import StrongPath (Abs, Dir, Path', relfile, (</>))
|
import StrongPath (Abs, Dir, Path', relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
import Wasp.AppSpec (AppSpec)
|
||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import Wasp.Generator.DbGenerator (genDb)
|
import Wasp.Generator.DbGenerator (genDb)
|
||||||
import qualified Wasp.Generator.DbGenerator as DbGenerator
|
import qualified Wasp.Generator.DbGenerator as DbGenerator
|
||||||
@ -23,21 +23,20 @@ import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
|||||||
import qualified Wasp.Generator.Setup
|
import qualified Wasp.Generator.Setup
|
||||||
import qualified Wasp.Generator.Start
|
import qualified Wasp.Generator.Start
|
||||||
import Wasp.Generator.WebAppGenerator (generateWebApp)
|
import Wasp.Generator.WebAppGenerator (generateWebApp)
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
|
|
||||||
-- | Generates web app code from given Wasp and writes it to given destination directory.
|
-- | Generates web app code from given Wasp and writes it to given destination directory.
|
||||||
-- If dstDir does not exist yet, it will be created.
|
-- If dstDir does not exist yet, it will be created.
|
||||||
-- NOTE(martin): What if there is already smth in the dstDir? It is probably best
|
-- NOTE(martin): What if there is already smth in the dstDir? It is probably best
|
||||||
-- if we clean it up first? But we don't want this to end up with us deleting stuff
|
-- if we clean it up first? But we don't want this to end up with us deleting stuff
|
||||||
-- from user's machine. Maybe we just overwrite and we are good?
|
-- from user's machine. Maybe we just overwrite and we are good?
|
||||||
writeWebAppCode :: Wasp -> Path' Abs (Dir ProjectRootDir) -> CompileOptions -> IO ()
|
writeWebAppCode :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ()
|
||||||
writeWebAppCode wasp dstDir compileOptions = do
|
writeWebAppCode spec dstDir = do
|
||||||
writeFileDrafts dstDir (generateWebApp wasp compileOptions)
|
writeFileDrafts dstDir (generateWebApp spec)
|
||||||
ServerGenerator.preCleanup wasp dstDir compileOptions
|
ServerGenerator.preCleanup spec dstDir
|
||||||
writeFileDrafts dstDir (genServer wasp compileOptions)
|
writeFileDrafts dstDir (genServer spec)
|
||||||
DbGenerator.preCleanup wasp dstDir compileOptions
|
DbGenerator.preCleanup spec dstDir
|
||||||
writeFileDrafts dstDir (genDb wasp compileOptions)
|
writeFileDrafts dstDir (genDb spec)
|
||||||
writeFileDrafts dstDir (genDockerFiles wasp compileOptions)
|
writeFileDrafts dstDir (genDockerFiles spec)
|
||||||
writeDotWaspInfo dstDir
|
writeDotWaspInfo dstDir
|
||||||
|
|
||||||
-- | Writes file drafts while using given destination dir as root dir.
|
-- | Writes file drafts while using given destination dir as root dir.
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.DbGenerator
|
module Wasp.Generator.DbGenerator
|
||||||
( genDb,
|
( genDb,
|
||||||
preCleanup,
|
preCleanup,
|
||||||
@ -9,22 +11,21 @@ where
|
|||||||
|
|
||||||
import Control.Monad (when)
|
import Control.Monad (when)
|
||||||
import Data.Aeson (object, (.=))
|
import Data.Aeson (object, (.=))
|
||||||
import Data.Maybe (isNothing, maybeToList)
|
import Data.Maybe (fromMaybe, isNothing, maybeToList)
|
||||||
import StrongPath (Abs, Dir, File', Path', Rel, reldir, relfile, (</>))
|
import StrongPath (Abs, Dir, File', Path', Rel, reldir, relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
import System.Directory (doesDirectoryExist, removeDirectoryRecursive)
|
import System.Directory (doesDirectoryExist, removeDirectoryRecursive)
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Db as AS.Db
|
||||||
|
import qualified Wasp.AppSpec.Entity as AS.Entity
|
||||||
import Wasp.Common (DbMigrationsDir)
|
import Wasp.Common (DbMigrationsDir)
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
|
||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft, createCopyDirFileDraft, createTemplateFileDraft)
|
import Wasp.Generator.FileDraft (FileDraft, createCopyDirFileDraft, createTemplateFileDraft)
|
||||||
import Wasp.Generator.Templates (TemplatesDir)
|
import Wasp.Generator.Templates (TemplatesDir)
|
||||||
import qualified Wasp.Psl.Ast.Model as Psl.Ast.Model
|
import qualified Wasp.Psl.Ast.Model as Psl.Ast.Model
|
||||||
import qualified Wasp.Psl.Generator.Model as Psl.Generator.Model
|
import qualified Wasp.Psl.Generator.Model as Psl.Generator.Model
|
||||||
import Wasp.Wasp (Wasp, getMigrationsDir)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.Db as Wasp.Db
|
|
||||||
import Wasp.Wasp.Entity (Entity)
|
|
||||||
import qualified Wasp.Wasp.Entity as Wasp.Entity
|
|
||||||
|
|
||||||
data DbRootDir
|
data DbRootDir
|
||||||
|
|
||||||
@ -50,13 +51,18 @@ dbSchemaFileInProjectRootDir = dbRootDirInProjectRootDir </> dbSchemaFileInDbRoo
|
|||||||
dbMigrationsDirInDbRootDir :: Path' (Rel DbRootDir) (Dir DbMigrationsDir)
|
dbMigrationsDirInDbRootDir :: Path' (Rel DbRootDir) (Dir DbMigrationsDir)
|
||||||
dbMigrationsDirInDbRootDir = [reldir|migrations|]
|
dbMigrationsDirInDbRootDir = [reldir|migrations|]
|
||||||
|
|
||||||
preCleanup :: Wasp -> Path' Abs (Dir ProjectRootDir) -> CompileOptions -> IO ()
|
preCleanup :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ()
|
||||||
preCleanup wasp projectRootDir _ = do
|
preCleanup spec projectRootDir = do
|
||||||
deleteGeneratedMigrationsDirIfRedundant wasp projectRootDir
|
deleteGeneratedMigrationsDirIfRedundant spec projectRootDir
|
||||||
|
|
||||||
deleteGeneratedMigrationsDirIfRedundant :: Wasp -> Path' Abs (Dir ProjectRootDir) -> IO ()
|
-- * Db generator
|
||||||
deleteGeneratedMigrationsDirIfRedundant wasp projectRootDir = do
|
|
||||||
let waspMigrationsDirMissing = isNothing $ getMigrationsDir wasp
|
genDb :: AppSpec -> [FileDraft]
|
||||||
|
genDb spec = genPrismaSchema spec : maybeToList (genMigrationsDir spec)
|
||||||
|
|
||||||
|
deleteGeneratedMigrationsDirIfRedundant :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ()
|
||||||
|
deleteGeneratedMigrationsDirIfRedundant spec projectRootDir = do
|
||||||
|
let waspMigrationsDirMissing = isNothing $ AS.migrationsDir spec
|
||||||
projectMigrationsDirExists <- doesDirectoryExist projectMigrationsDirAbsFilePath
|
projectMigrationsDirExists <- doesDirectoryExist projectMigrationsDirAbsFilePath
|
||||||
when (waspMigrationsDirMissing && projectMigrationsDirExists) $ do
|
when (waspMigrationsDirMissing && projectMigrationsDirExists) $ do
|
||||||
putStrLn "A migrations directory does not exist in this Wasp root directory, but does in the generated project output directory."
|
putStrLn "A migrations directory does not exist in this Wasp root directory, but does in the generated project output directory."
|
||||||
@ -66,40 +72,36 @@ deleteGeneratedMigrationsDirIfRedundant wasp projectRootDir = do
|
|||||||
where
|
where
|
||||||
projectMigrationsDirAbsFilePath = SP.fromAbsDir $ projectRootDir </> dbRootDirInProjectRootDir </> dbMigrationsDirInDbRootDir
|
projectMigrationsDirAbsFilePath = SP.fromAbsDir $ projectRootDir </> dbRootDirInProjectRootDir </> dbMigrationsDirInDbRootDir
|
||||||
|
|
||||||
genDb :: Wasp -> CompileOptions -> [FileDraft]
|
genPrismaSchema :: AppSpec -> FileDraft
|
||||||
genDb wasp _ =
|
genPrismaSchema spec = createTemplateFileDraft dstPath tmplSrcPath (Just templateData)
|
||||||
genPrismaSchema wasp : maybeToList (genMigrationsDir wasp)
|
|
||||||
|
|
||||||
genPrismaSchema :: Wasp -> FileDraft
|
|
||||||
genPrismaSchema wasp = createTemplateFileDraft dstPath tmplSrcPath (Just templateData)
|
|
||||||
where
|
where
|
||||||
dstPath = dbSchemaFileInProjectRootDir
|
dstPath = dbSchemaFileInProjectRootDir
|
||||||
tmplSrcPath = dbTemplatesDirInTemplatesDir </> dbSchemaFileInDbTemplatesDir
|
tmplSrcPath = dbTemplatesDirInTemplatesDir </> dbSchemaFileInDbTemplatesDir
|
||||||
|
|
||||||
templateData =
|
templateData =
|
||||||
object
|
object
|
||||||
[ "modelSchemas" .= map entityToPslModelSchema (Wasp.getPSLEntities wasp),
|
[ "modelSchemas" .= map entityToPslModelSchema (AS.getDecls @AS.Entity.Entity spec),
|
||||||
"datasourceProvider" .= (datasourceProvider :: String),
|
"datasourceProvider" .= (datasourceProvider :: String),
|
||||||
"datasourceUrl" .= (datasourceUrl :: String)
|
"datasourceUrl" .= (datasourceUrl :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
dbSystem = maybe Wasp.Db.SQLite Wasp.Db._system (Wasp.getDb wasp)
|
dbSystem = fromMaybe AS.Db.SQLite (AS.Db.system =<< AS.App.db (snd $ AS.getApp spec))
|
||||||
(datasourceProvider, datasourceUrl) = case dbSystem of
|
(datasourceProvider, datasourceUrl) = case dbSystem of
|
||||||
Wasp.Db.PostgreSQL -> ("postgresql", "env(\"DATABASE_URL\")")
|
AS.Db.PostgreSQL -> ("postgresql", "env(\"DATABASE_URL\")")
|
||||||
-- TODO: Report this error with some better mechanism, not `error`.
|
-- TODO: Report this error with some better mechanism, not `error`.
|
||||||
Wasp.Db.SQLite ->
|
AS.Db.SQLite ->
|
||||||
if Wasp.getIsBuild wasp
|
if AS.isBuild spec
|
||||||
then error "SQLite (a default database) is not supported in production. To build your Wasp app for production, switch to a different database. Switching to PostgreSQL: https://wasp-lang.dev/docs/language/basic-elements/#migrating-from-sqlite-to-postgresql ."
|
then error "SQLite (a default database) is not supported in production. To build your Wasp app for production, switch to a different database. Switching to PostgreSQL: https://wasp-lang.dev/docs/language/features/#migrating-from-sqlite-to-postgresql ."
|
||||||
else ("sqlite", "\"file:./dev.db\"")
|
else ("sqlite", "\"file:./dev.db\"")
|
||||||
|
|
||||||
entityToPslModelSchema :: Entity -> String
|
entityToPslModelSchema :: (String, AS.Entity.Entity) -> String
|
||||||
entityToPslModelSchema entity =
|
entityToPslModelSchema (entityName, entity) =
|
||||||
Psl.Generator.Model.generateModel $
|
Psl.Generator.Model.generateModel $
|
||||||
Psl.Ast.Model.Model (Wasp.Entity._name entity) (Wasp.Entity._pslModelBody entity)
|
Psl.Ast.Model.Model entityName (AS.Entity.getPslModelBody entity)
|
||||||
|
|
||||||
genMigrationsDir :: Wasp -> Maybe FileDraft
|
genMigrationsDir :: AppSpec -> Maybe FileDraft
|
||||||
genMigrationsDir wasp =
|
genMigrationsDir spec =
|
||||||
(getMigrationsDir wasp) >>= \waspMigrationsDir ->
|
AS.migrationsDir spec >>= \waspMigrationsDir ->
|
||||||
Just $ createCopyDirFileDraft (SP.castDir genProjectMigrationsDir) (SP.castDir waspMigrationsDir)
|
Just $ createCopyDirFileDraft (SP.castDir genProjectMigrationsDir) (SP.castDir waspMigrationsDir)
|
||||||
where
|
where
|
||||||
genProjectMigrationsDir = dbRootDirInProjectRootDir </> dbMigrationsDirInDbRootDir
|
genProjectMigrationsDir = dbRootDirInProjectRootDir </> dbMigrationsDirInDbRootDir
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.DockerGenerator
|
module Wasp.Generator.DockerGenerator
|
||||||
( genDockerFiles,
|
( genDockerFiles,
|
||||||
)
|
)
|
||||||
@ -5,29 +7,29 @@ where
|
|||||||
|
|
||||||
import Data.Aeson (object, (.=))
|
import Data.Aeson (object, (.=))
|
||||||
import StrongPath (File', Path', Rel, relfile)
|
import StrongPath (File', Path', Rel, relfile)
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.Entity as AS.Entity
|
||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
||||||
import Wasp.Generator.Templates (TemplatesDir)
|
import Wasp.Generator.Templates (TemplatesDir)
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
|
|
||||||
genDockerFiles :: Wasp -> CompileOptions -> [FileDraft]
|
genDockerFiles :: AppSpec -> [FileDraft]
|
||||||
genDockerFiles wasp _ = genDockerfile wasp : [genDockerignore wasp]
|
genDockerFiles spec = genDockerfile spec : [genDockerignore spec]
|
||||||
|
|
||||||
-- TODO: Inject paths to server and db files/dirs, right now they are hardcoded in the templates.
|
-- TODO: Inject paths to server and db files/dirs, right now they are hardcoded in the templates.
|
||||||
genDockerfile :: Wasp -> FileDraft
|
genDockerfile :: AppSpec -> FileDraft
|
||||||
genDockerfile wasp =
|
genDockerfile spec =
|
||||||
createTemplateFileDraft
|
createTemplateFileDraft
|
||||||
([relfile|Dockerfile|] :: Path' (Rel ProjectRootDir) File')
|
([relfile|Dockerfile|] :: Path' (Rel ProjectRootDir) File')
|
||||||
([relfile|Dockerfile|] :: Path' (Rel TemplatesDir) File')
|
([relfile|Dockerfile|] :: Path' (Rel TemplatesDir) File')
|
||||||
( Just $
|
( Just $
|
||||||
object
|
object
|
||||||
[ "usingPrisma" .= not (null $ Wasp.getPSLEntities wasp)
|
[ "usingPrisma" .= not (null $ AS.getDecls @AS.Entity.Entity spec)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
genDockerignore :: Wasp -> FileDraft
|
genDockerignore :: AppSpec -> FileDraft
|
||||||
genDockerignore _ =
|
genDockerignore _ =
|
||||||
createTemplateFileDraft
|
createTemplateFileDraft
|
||||||
([relfile|.dockerignore|] :: Path' (Rel ProjectRootDir) File')
|
([relfile|.dockerignore|] :: Path' (Rel ProjectRootDir) File')
|
||||||
|
@ -10,17 +10,14 @@ import qualified Wasp.AppSpec.ExternalCode as EC
|
|||||||
import qualified Wasp.Generator.ExternalCodeGenerator.Common as C
|
import qualified Wasp.Generator.ExternalCodeGenerator.Common as C
|
||||||
import Wasp.Generator.ExternalCodeGenerator.Js (generateJsFile)
|
import Wasp.Generator.ExternalCodeGenerator.Js (generateJsFile)
|
||||||
import qualified Wasp.Generator.FileDraft as FD
|
import qualified Wasp.Generator.FileDraft as FD
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
|
|
||||||
-- | Takes external code files from Wasp and generates them in new location as part of the generated project.
|
-- | Takes external code files from Wasp and generates them in new location as part of the generated project.
|
||||||
-- It might not just copy them but also do some changes on them, as needed.
|
-- It might not just copy them but also do some changes on them, as needed.
|
||||||
generateExternalCodeDir ::
|
generateExternalCodeDir ::
|
||||||
C.ExternalCodeGeneratorStrategy ->
|
C.ExternalCodeGeneratorStrategy ->
|
||||||
Wasp ->
|
[EC.File] ->
|
||||||
[FD.FileDraft]
|
[FD.FileDraft]
|
||||||
generateExternalCodeDir strategy wasp =
|
generateExternalCodeDir strategy = map (generateFile strategy)
|
||||||
map (generateFile strategy) (Wasp.getExternalCodeFiles wasp)
|
|
||||||
|
|
||||||
generateFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> FD.FileDraft
|
generateFile :: C.ExternalCodeGeneratorStrategy -> EC.File -> FD.FileDraft
|
||||||
generateFile strategy file
|
generateFile strategy file
|
||||||
|
@ -1,27 +1,26 @@
|
|||||||
module Wasp.Generator.JsImport
|
module Wasp.Generator.JsImport
|
||||||
( getImportDetailsForJsFnImport,
|
( getJsImportDetailsForExtFnImport,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import StrongPath (Dir, Path, Posix, Rel, (</>))
|
import StrongPath (Dir, Path, Posix, Rel, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
|
import qualified Wasp.AppSpec.ExtImport as AS.ExtImport
|
||||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||||
import qualified Wasp.Wasp.JsImport as Wasp.JsImport
|
|
||||||
|
|
||||||
getImportDetailsForJsFnImport ::
|
getJsImportDetailsForExtFnImport ::
|
||||||
-- | Path to generated external code directory, relative to the directory in which file doing the importing is.
|
-- | Path to generated external code directory, relative to the directory in which file doing the importing is.
|
||||||
Path Posix (Rel (Dir a)) (Dir GeneratedExternalCodeDir) ->
|
Path Posix (Rel (Dir a)) (Dir GeneratedExternalCodeDir) ->
|
||||||
Wasp.JsImport.JsImport ->
|
AS.ExtImport.ExtImport ->
|
||||||
-- | (importIdentifier, importStmt)
|
-- | (importIdentifier, importStmt)
|
||||||
-- - importIdentifier -> Identifier via which you can access js function after you import it with importStmt.
|
-- - importIdentifier -> Identifier via which you can access ext js function after you import it with importStmt.
|
||||||
-- - importStmt -> Import statement via which you should do the import.
|
-- - importStmt -> Javascript import statement via which you should do the import.
|
||||||
(String, String)
|
(String, String)
|
||||||
getImportDetailsForJsFnImport relPosixPathToExtCodeDir jsImport = (importIdentifier, importStmt)
|
getJsImportDetailsForExtFnImport relPosixPathToExtCodeDir extImport = (importIdentifier, importStmt)
|
||||||
where
|
where
|
||||||
importStmt = "import " ++ importWhat ++ " from '" ++ importFrom ++ "'"
|
importStmt = "import " ++ importWhat ++ " from '" ++ importFrom ++ "'"
|
||||||
importFrom = "./" ++ SP.fromRelFileP (relPosixPathToExtCodeDir </> SP.castRel (Wasp.JsImport._from jsImport))
|
importFrom = "./" ++ SP.fromRelFileP (relPosixPathToExtCodeDir </> SP.castRel (AS.ExtImport.path extImport))
|
||||||
(importIdentifier, importWhat) =
|
(importIdentifier, importWhat) =
|
||||||
case (Wasp.JsImport._defaultImport jsImport, Wasp.JsImport._namedImports jsImport) of
|
case AS.ExtImport.name extImport of
|
||||||
(Just defaultImport, []) -> (defaultImport, defaultImport)
|
AS.ExtImport.ExtImportModule defaultImport -> (defaultImport, defaultImport)
|
||||||
(Nothing, [namedImport]) -> (namedImport, "{ " ++ namedImport ++ " }")
|
AS.ExtImport.ExtImportField namedImport -> (namedImport, "{ " ++ namedImport ++ " }")
|
||||||
_ -> error $ "Expected from " ++ show jsImport ++ " to be either default import or single named import, due to it being used to import a single js function."
|
|
||||||
|
@ -8,7 +8,7 @@ where
|
|||||||
import Data.Bifunctor (second)
|
import Data.Bifunctor (second)
|
||||||
import Data.List (find, intercalate)
|
import Data.List (find, intercalate)
|
||||||
import Data.Maybe (fromJust, isJust)
|
import Data.Maybe (fromJust, isJust)
|
||||||
import qualified Wasp.NpmDependency as ND
|
import qualified Wasp.AppSpec.App.Dependency as D
|
||||||
|
|
||||||
type NpmDependenciesConflictError = String
|
type NpmDependenciesConflictError = String
|
||||||
|
|
||||||
@ -20,56 +20,56 @@ type NpmDependenciesConflictError = String
|
|||||||
-- On error (Left), returns list of conflicting user deps together with the error message
|
-- On error (Left), returns list of conflicting user deps together with the error message
|
||||||
-- explaining what the error is.
|
-- explaining what the error is.
|
||||||
resolveNpmDeps ::
|
resolveNpmDeps ::
|
||||||
[ND.NpmDependency] ->
|
[D.Dependency] ->
|
||||||
[ND.NpmDependency] ->
|
[D.Dependency] ->
|
||||||
Either
|
Either
|
||||||
[(ND.NpmDependency, NpmDependenciesConflictError)]
|
[(D.Dependency, NpmDependenciesConflictError)]
|
||||||
([ND.NpmDependency], [ND.NpmDependency])
|
([D.Dependency], [D.Dependency])
|
||||||
resolveNpmDeps waspDeps userDeps =
|
resolveNpmDeps waspDeps userDeps =
|
||||||
if null conflictingUserDeps
|
if null conflictingUserDeps
|
||||||
then Right (waspDeps, userDepsNotInWaspDeps)
|
then Right (waspDeps, userDepsNotInWaspDeps)
|
||||||
else Left conflictingUserDeps
|
else Left conflictingUserDeps
|
||||||
where
|
where
|
||||||
conflictingUserDeps :: [(ND.NpmDependency, NpmDependenciesConflictError)]
|
conflictingUserDeps :: [(D.Dependency, NpmDependenciesConflictError)]
|
||||||
conflictingUserDeps =
|
conflictingUserDeps =
|
||||||
map (second fromJust) $
|
map (second fromJust) $
|
||||||
filter (isJust . snd) $
|
filter (isJust . snd) $
|
||||||
map (\dep -> (dep, checkIfConflictingUserDep dep)) userDeps
|
map (\dep -> (dep, checkIfConflictingUserDep dep)) userDeps
|
||||||
|
|
||||||
checkIfConflictingUserDep :: ND.NpmDependency -> Maybe NpmDependenciesConflictError
|
checkIfConflictingUserDep :: D.Dependency -> Maybe NpmDependenciesConflictError
|
||||||
checkIfConflictingUserDep userDep =
|
checkIfConflictingUserDep userDep =
|
||||||
let attachErrorMessage dep =
|
let attachErrorMessage dep =
|
||||||
"Error: Dependency conflict for user npm dependency ("
|
"Error: Dependency conflict for user dependency ("
|
||||||
++ ND._name dep
|
++ D.name dep
|
||||||
++ ", "
|
++ ", "
|
||||||
++ ND._version dep
|
++ D.version dep
|
||||||
++ "): "
|
++ "): "
|
||||||
++ "Version must be set to the exactly the same version as"
|
++ "Version must be set to the exactly the same version as"
|
||||||
++ " the one wasp is using: "
|
++ " the one wasp is using: "
|
||||||
++ ND._version dep
|
++ D.version dep
|
||||||
in attachErrorMessage <$> find (areTwoDepsInConflict userDep) waspDeps
|
in attachErrorMessage <$> find (areTwoDepsInConflict userDep) waspDeps
|
||||||
|
|
||||||
areTwoDepsInConflict :: ND.NpmDependency -> ND.NpmDependency -> Bool
|
areTwoDepsInConflict :: D.Dependency -> D.Dependency -> Bool
|
||||||
areTwoDepsInConflict d1 d2 =
|
areTwoDepsInConflict d1 d2 =
|
||||||
ND._name d1 == ND._name d2
|
D.name d1 == D.name d2
|
||||||
&& ND._version d1 /= ND._version d2
|
&& D.version d1 /= D.version d2
|
||||||
|
|
||||||
userDepsNotInWaspDeps :: [ND.NpmDependency]
|
userDepsNotInWaspDeps :: [D.Dependency]
|
||||||
userDepsNotInWaspDeps = filter (not . isDepWithNameInWaspDeps . ND._name) userDeps
|
userDepsNotInWaspDeps = filter (not . isDepWithNameInWaspDeps . D.name) userDeps
|
||||||
|
|
||||||
isDepWithNameInWaspDeps :: String -> Bool
|
isDepWithNameInWaspDeps :: String -> Bool
|
||||||
isDepWithNameInWaspDeps name = any ((name ==) . ND._name) waspDeps
|
isDepWithNameInWaspDeps name = any ((name ==) . D.name) waspDeps
|
||||||
|
|
||||||
npmDepsToPackageJsonEntryWithKey :: [ND.NpmDependency] -> String -> String
|
npmDepsToPackageJsonEntryWithKey :: [D.Dependency] -> String -> String
|
||||||
npmDepsToPackageJsonEntryWithKey deps key =
|
npmDepsToPackageJsonEntryWithKey deps key =
|
||||||
"\""
|
"\""
|
||||||
++ key
|
++ key
|
||||||
++ "\": {"
|
++ "\": {"
|
||||||
++ intercalate ",\n " (map (\dep -> "\"" ++ ND._name dep ++ "\": \"" ++ ND._version dep ++ "\"") deps)
|
++ intercalate ",\n " (map (\dep -> "\"" ++ D.name dep ++ "\": \"" ++ D.version dep ++ "\"") deps)
|
||||||
++ "\n}"
|
++ "\n}"
|
||||||
|
|
||||||
npmDepsToPackageJsonEntry :: [ND.NpmDependency] -> String
|
npmDepsToPackageJsonEntry :: [D.Dependency] -> String
|
||||||
npmDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "dependencies"
|
npmDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "dependencies"
|
||||||
|
|
||||||
npmDevDepsToPackageJsonEntry :: [ND.NpmDependency] -> String
|
npmDevDepsToPackageJsonEntry :: [D.Dependency] -> String
|
||||||
npmDevDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "devDependencies"
|
npmDevDepsToPackageJsonEntry deps = npmDepsToPackageJsonEntryWithKey deps "devDependencies"
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.ServerGenerator
|
module Wasp.Generator.ServerGenerator
|
||||||
( genServer,
|
( genServer,
|
||||||
preCleanup,
|
preCleanup,
|
||||||
@ -20,12 +22,18 @@ import qualified StrongPath as SP
|
|||||||
import System.Directory (removeFile)
|
import System.Directory (removeFile)
|
||||||
import System.IO.Error (isDoesNotExistError)
|
import System.IO.Error (isDoesNotExistError)
|
||||||
import UnliftIO.Exception (catch, throwIO)
|
import UnliftIO.Exception (catch, throwIO)
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Auth as AS.App.Auth
|
||||||
|
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||||
|
import qualified Wasp.AppSpec.App.Server as AS.App.Server
|
||||||
|
import qualified Wasp.AppSpec.Entity as AS.Entity
|
||||||
import Wasp.Generator.Common (ProjectRootDir, nodeVersionAsText)
|
import Wasp.Generator.Common (ProjectRootDir, nodeVersionAsText)
|
||||||
import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
||||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft)
|
import Wasp.Generator.FileDraft (FileDraft, createCopyFileDraft)
|
||||||
import Wasp.Generator.JsImport (getImportDetailsForJsFnImport)
|
import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||||
import Wasp.Generator.PackageJsonGenerator
|
import Wasp.Generator.PackageJsonGenerator
|
||||||
( npmDepsToPackageJsonEntry,
|
( npmDepsToPackageJsonEntry,
|
||||||
npmDevDepsToPackageJsonEntry,
|
npmDevDepsToPackageJsonEntry,
|
||||||
@ -42,24 +50,18 @@ import Wasp.Generator.ServerGenerator.ConfigG (genConfigFile)
|
|||||||
import qualified Wasp.Generator.ServerGenerator.ExternalCodeGenerator as ServerExternalCodeGenerator
|
import qualified Wasp.Generator.ServerGenerator.ExternalCodeGenerator as ServerExternalCodeGenerator
|
||||||
import Wasp.Generator.ServerGenerator.OperationsG (genOperations)
|
import Wasp.Generator.ServerGenerator.OperationsG (genOperations)
|
||||||
import Wasp.Generator.ServerGenerator.OperationsRoutesG (genOperationsRoutes)
|
import Wasp.Generator.ServerGenerator.OperationsRoutesG (genOperationsRoutes)
|
||||||
import qualified Wasp.NpmDependency as ND
|
|
||||||
import Wasp.Wasp (Wasp, getAuth)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
import qualified Wasp.Wasp.NpmDependencies as WND
|
|
||||||
import qualified Wasp.Wasp.Server as Wasp.Server
|
|
||||||
|
|
||||||
genServer :: Wasp -> CompileOptions -> [FileDraft]
|
genServer :: AppSpec -> [FileDraft]
|
||||||
genServer wasp _ =
|
genServer spec =
|
||||||
concat
|
concat
|
||||||
[ [genReadme wasp],
|
[ [genReadme],
|
||||||
[genPackageJson wasp waspNpmDeps waspNpmDevDeps],
|
[genPackageJson spec waspNpmDeps waspNpmDevDeps],
|
||||||
[genNpmrc wasp],
|
[genNpmrc],
|
||||||
[genNvmrc wasp],
|
[genNvmrc],
|
||||||
[genGitignore wasp],
|
[genGitignore],
|
||||||
genSrcDir wasp,
|
genSrcDir spec,
|
||||||
generateExternalCodeDir ServerExternalCodeGenerator.generatorStrategy wasp,
|
generateExternalCodeDir ServerExternalCodeGenerator.generatorStrategy (AS.externalCodeFiles spec),
|
||||||
genDotEnv wasp
|
genDotEnv spec
|
||||||
]
|
]
|
||||||
|
|
||||||
-- Cleanup to be performed before generating new server code.
|
-- Cleanup to be performed before generating new server code.
|
||||||
@ -67,8 +69,8 @@ genServer wasp _ =
|
|||||||
-- TODO: Once we implement a fancier method of removing old/redundant files in outDir,
|
-- TODO: Once we implement a fancier method of removing old/redundant files in outDir,
|
||||||
-- we will not need this method any more. Check https://github.com/wasp-lang/wasp/issues/209
|
-- we will not need this method any more. Check https://github.com/wasp-lang/wasp/issues/209
|
||||||
-- for progress of this.
|
-- for progress of this.
|
||||||
preCleanup :: Wasp -> Path' Abs (Dir ProjectRootDir) -> CompileOptions -> IO ()
|
preCleanup :: AppSpec -> Path' Abs (Dir ProjectRootDir) -> IO ()
|
||||||
preCleanup _ outDir _ = do
|
preCleanup _ outDir = do
|
||||||
-- If .env gets removed but there is old .env file in generated project from previous attempts,
|
-- If .env gets removed but there is old .env file in generated project from previous attempts,
|
||||||
-- we need to make sure we remove it.
|
-- we need to make sure we remove it.
|
||||||
removeFile dotEnvAbsFilePath
|
removeFile dotEnvAbsFilePath
|
||||||
@ -76,9 +78,9 @@ preCleanup _ outDir _ = do
|
|||||||
where
|
where
|
||||||
dotEnvAbsFilePath = SP.toFilePath $ outDir </> C.serverRootDirInProjectRootDir </> dotEnvInServerRootDir
|
dotEnvAbsFilePath = SP.toFilePath $ outDir </> C.serverRootDirInProjectRootDir </> dotEnvInServerRootDir
|
||||||
|
|
||||||
genDotEnv :: Wasp -> [FileDraft]
|
genDotEnv :: AppSpec -> [FileDraft]
|
||||||
genDotEnv wasp =
|
genDotEnv spec =
|
||||||
case Wasp.getDotEnvFile wasp of
|
case AS.dotEnvFile spec of
|
||||||
Just srcFilePath ->
|
Just srcFilePath ->
|
||||||
[ createCopyFileDraft
|
[ createCopyFileDraft
|
||||||
(C.serverRootDirInProjectRootDir </> dotEnvInServerRootDir)
|
(C.serverRootDirInProjectRootDir </> dotEnvInServerRootDir)
|
||||||
@ -89,22 +91,21 @@ genDotEnv wasp =
|
|||||||
dotEnvInServerRootDir :: Path' (Rel C.ServerRootDir) File'
|
dotEnvInServerRootDir :: Path' (Rel C.ServerRootDir) File'
|
||||||
dotEnvInServerRootDir = [relfile|.env|]
|
dotEnvInServerRootDir = [relfile|.env|]
|
||||||
|
|
||||||
genReadme :: Wasp -> FileDraft
|
genReadme :: FileDraft
|
||||||
genReadme _ = C.copyTmplAsIs (asTmplFile [relfile|README.md|])
|
genReadme = C.mkTmplFd (asTmplFile [relfile|README.md|])
|
||||||
|
|
||||||
genPackageJson :: Wasp -> [ND.NpmDependency] -> [ND.NpmDependency] -> FileDraft
|
genPackageJson :: AppSpec -> [AS.Dependency.Dependency] -> [AS.Dependency.Dependency] -> FileDraft
|
||||||
genPackageJson wasp waspDeps waspDevDeps =
|
genPackageJson spec waspDeps waspDevDeps =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|package.json|])
|
(asTmplFile [relfile|package.json|])
|
||||||
(asServerFile [relfile|package.json|])
|
(asServerFile [relfile|package.json|])
|
||||||
( Just $
|
( Just $
|
||||||
object
|
object
|
||||||
[ "wasp" .= wasp,
|
[ "depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps),
|
||||||
"depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps),
|
|
||||||
"devDepsChunk" .= npmDevDepsToPackageJsonEntry waspDevDeps,
|
"devDepsChunk" .= npmDevDepsToPackageJsonEntry waspDevDeps,
|
||||||
"nodeVersion" .= nodeVersionAsText,
|
"nodeVersion" .= nodeVersionAsText,
|
||||||
"startProductionScript"
|
"startProductionScript"
|
||||||
.= if not (null $ Wasp.getPSLEntities wasp)
|
.= if not (null $ AS.getDecls @AS.Entity.Entity spec)
|
||||||
then "npm run db-migrate-prod && "
|
then "npm run db-migrate-prod && "
|
||||||
else
|
else
|
||||||
""
|
""
|
||||||
@ -117,12 +118,12 @@ genPackageJson wasp waspDeps waspDevDeps =
|
|||||||
Right deps -> deps
|
Right deps -> deps
|
||||||
Left depsAndErrors -> error $ intercalate " ; " $ map snd depsAndErrors
|
Left depsAndErrors -> error $ intercalate " ; " $ map snd depsAndErrors
|
||||||
|
|
||||||
userDeps :: [ND.NpmDependency]
|
userDeps :: [AS.Dependency.Dependency]
|
||||||
userDeps = WND._dependencies $ Wasp.getNpmDependencies wasp
|
userDeps = fromMaybe [] $ AS.App.dependencies $ snd $ AS.getApp spec
|
||||||
|
|
||||||
waspNpmDeps :: [ND.NpmDependency]
|
waspNpmDeps :: [AS.Dependency.Dependency]
|
||||||
waspNpmDeps =
|
waspNpmDeps =
|
||||||
ND.fromList
|
AS.Dependency.fromList
|
||||||
[ ("cookie-parser", "~1.4.4"),
|
[ ("cookie-parser", "~1.4.4"),
|
||||||
("cors", "^2.8.5"),
|
("cors", "^2.8.5"),
|
||||||
("debug", "~2.6.9"),
|
("debug", "~2.6.9"),
|
||||||
@ -135,56 +136,56 @@ waspNpmDeps =
|
|||||||
("helmet", "^4.6.0")
|
("helmet", "^4.6.0")
|
||||||
]
|
]
|
||||||
|
|
||||||
waspNpmDevDeps :: [ND.NpmDependency]
|
waspNpmDevDeps :: [AS.Dependency.Dependency]
|
||||||
waspNpmDevDeps =
|
waspNpmDevDeps =
|
||||||
ND.fromList
|
AS.Dependency.fromList
|
||||||
[ ("nodemon", "^2.0.4"),
|
[ ("nodemon", "^2.0.4"),
|
||||||
("standard", "^14.3.4"),
|
("standard", "^14.3.4"),
|
||||||
("prisma", "2.22.1")
|
("prisma", "2.22.1")
|
||||||
]
|
]
|
||||||
|
|
||||||
genNpmrc :: Wasp -> FileDraft
|
genNpmrc :: FileDraft
|
||||||
genNpmrc _ =
|
genNpmrc =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|npmrc|])
|
(asTmplFile [relfile|npmrc|])
|
||||||
(asServerFile [relfile|.npmrc|])
|
(asServerFile [relfile|.npmrc|])
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
genNvmrc :: Wasp -> FileDraft
|
genNvmrc :: FileDraft
|
||||||
genNvmrc _ =
|
genNvmrc =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|nvmrc|])
|
(asTmplFile [relfile|nvmrc|])
|
||||||
(asServerFile [relfile|.nvmrc|])
|
(asServerFile [relfile|.nvmrc|])
|
||||||
(Just (object ["nodeVersion" .= ('v' : nodeVersionAsText)]))
|
(Just (object ["nodeVersion" .= ('v' : nodeVersionAsText)]))
|
||||||
|
|
||||||
genGitignore :: Wasp -> FileDraft
|
genGitignore :: FileDraft
|
||||||
genGitignore _ =
|
genGitignore =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|gitignore|])
|
(asTmplFile [relfile|gitignore|])
|
||||||
(asServerFile [relfile|.gitignore|])
|
(asServerFile [relfile|.gitignore|])
|
||||||
Nothing
|
Nothing
|
||||||
|
|
||||||
genSrcDir :: Wasp -> [FileDraft]
|
genSrcDir :: AppSpec -> [FileDraft]
|
||||||
genSrcDir wasp =
|
genSrcDir spec =
|
||||||
concat
|
concat
|
||||||
[ [C.copySrcTmplAsIs $ C.asTmplSrcFile [relfile|app.js|]],
|
[ [C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|app.js|]],
|
||||||
[C.copySrcTmplAsIs $ C.asTmplSrcFile [relfile|server.js|]],
|
[C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|server.js|]],
|
||||||
[C.copySrcTmplAsIs $ C.asTmplSrcFile [relfile|utils.js|]],
|
[C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|utils.js|]],
|
||||||
[C.copySrcTmplAsIs $ C.asTmplSrcFile [relfile|core/AuthError.js|]],
|
[C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|core/AuthError.js|]],
|
||||||
[C.copySrcTmplAsIs $ C.asTmplSrcFile [relfile|core/HttpError.js|]],
|
[C.mkSrcTmplFd $ C.asTmplSrcFile [relfile|core/HttpError.js|]],
|
||||||
[genDbClient wasp],
|
[genDbClient spec],
|
||||||
[genConfigFile wasp],
|
[genConfigFile spec],
|
||||||
genRoutesDir wasp,
|
genRoutesDir spec,
|
||||||
genOperationsRoutes wasp,
|
genOperationsRoutes spec,
|
||||||
genOperations wasp,
|
genOperations spec,
|
||||||
genAuth wasp,
|
genAuth spec,
|
||||||
[genServerJs wasp]
|
[genServerJs spec]
|
||||||
]
|
]
|
||||||
|
|
||||||
genDbClient :: Wasp -> FileDraft
|
genDbClient :: AppSpec -> FileDraft
|
||||||
genDbClient wasp = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genDbClient spec = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
maybeAuth = getAuth wasp
|
maybeAuth = AS.App.auth $ snd $ AS.getApp spec
|
||||||
|
|
||||||
dbClientRelToSrcP = [relfile|dbClient.js|]
|
dbClientRelToSrcP = [relfile|dbClient.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> dbClientRelToSrcP
|
tmplFile = C.asTmplFile $ [reldir|src|] </> dbClientRelToSrcP
|
||||||
@ -195,13 +196,13 @@ genDbClient wasp = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
|||||||
then
|
then
|
||||||
object
|
object
|
||||||
[ "isAuthEnabled" .= True,
|
[ "isAuthEnabled" .= True,
|
||||||
"userEntityUpper" .= Wasp.Auth._userEntity (fromJust maybeAuth)
|
"userEntityUpper" .= (AS.refName (AS.App.Auth.userEntity $ fromJust maybeAuth) :: String)
|
||||||
]
|
]
|
||||||
else object []
|
else object []
|
||||||
|
|
||||||
genServerJs :: Wasp -> FileDraft
|
genServerJs :: AppSpec -> FileDraft
|
||||||
genServerJs wasp =
|
genServerJs spec =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|src/server.js|])
|
(asTmplFile [relfile|src/server.js|])
|
||||||
(asServerFile [relfile|src/server.js|])
|
(asServerFile [relfile|src/server.js|])
|
||||||
( Just $
|
( Just $
|
||||||
@ -212,8 +213,8 @@ genServerJs wasp =
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
maybeSetupJsFunction = Wasp.Server._setupJsFunction <$> Wasp.getServer wasp
|
maybeSetupJsFunction = AS.App.Server.setupFn =<< AS.App.server (snd $ AS.getApp spec)
|
||||||
maybeSetupJsFnImportDetails = getImportDetailsForJsFnImport relPosixPathFromSrcDirToExtSrcDir <$> maybeSetupJsFunction
|
maybeSetupJsFnImportDetails = getJsImportDetailsForExtFnImport relPosixPathFromSrcDirToExtSrcDir <$> maybeSetupJsFunction
|
||||||
(maybeSetupJsFnImportIdentifier, maybeSetupJsFnImportStmt) =
|
(maybeSetupJsFnImportIdentifier, maybeSetupJsFnImportStmt) =
|
||||||
(fst <$> maybeSetupJsFnImportDetails, snd <$> maybeSetupJsFnImportDetails)
|
(fst <$> maybeSetupJsFnImportDetails, snd <$> maybeSetupJsFnImportDetails)
|
||||||
|
|
||||||
@ -221,17 +222,17 @@ genServerJs wasp =
|
|||||||
relPosixPathFromSrcDirToExtSrcDir :: Path Posix (Rel (Dir ServerSrcDir)) (Dir GeneratedExternalCodeDir)
|
relPosixPathFromSrcDirToExtSrcDir :: Path Posix (Rel (Dir ServerSrcDir)) (Dir GeneratedExternalCodeDir)
|
||||||
relPosixPathFromSrcDirToExtSrcDir = [reldirP|./ext-src|]
|
relPosixPathFromSrcDirToExtSrcDir = [reldirP|./ext-src|]
|
||||||
|
|
||||||
genRoutesDir :: Wasp -> [FileDraft]
|
genRoutesDir :: AppSpec -> [FileDraft]
|
||||||
genRoutesDir wasp =
|
genRoutesDir spec =
|
||||||
-- TODO(martin): We will probably want to extract "routes" path here same as we did with "src", to avoid hardcoding,
|
-- TODO(martin): We will probably want to extract "routes" path here same as we did with "src", to avoid hardcoding,
|
||||||
-- but I did not bother with it yet since it is used only here for now.
|
-- but I did not bother with it yet since it is used only here for now.
|
||||||
[ C.makeTemplateFD
|
[ C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|src/routes/index.js|])
|
(asTmplFile [relfile|src/routes/index.js|])
|
||||||
(asServerFile [relfile|src/routes/index.js|])
|
(asServerFile [relfile|src/routes/index.js|])
|
||||||
( Just $
|
( Just $
|
||||||
object
|
object
|
||||||
[ "operationsRouteInRootRouter" .= operationsRouteInRootRouter,
|
[ "operationsRouteInRootRouter" .= (operationsRouteInRootRouter :: String),
|
||||||
"isAuthEnabled" .= isJust (getAuth wasp)
|
"isAuthEnabled" .= (AS.isAuthEnabled spec :: Bool)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
@ -5,14 +5,16 @@ where
|
|||||||
|
|
||||||
import Data.Aeson (object, (.=))
|
import Data.Aeson (object, (.=))
|
||||||
import StrongPath (reldir, relfile, (</>))
|
import StrongPath (reldir, relfile, (</>))
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Auth as AS.Auth
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||||
import qualified Wasp.Util as Util
|
import qualified Wasp.Util as Util
|
||||||
import Wasp.Wasp (Wasp, getAuth)
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
|
|
||||||
genAuth :: Wasp -> [FileDraft]
|
genAuth :: AppSpec -> [FileDraft]
|
||||||
genAuth wasp = case maybeAuth of
|
genAuth spec = case maybeAuth of
|
||||||
Just auth ->
|
Just auth ->
|
||||||
[ genCoreAuth auth,
|
[ genCoreAuth auth,
|
||||||
genAuthMiddleware auth,
|
genAuthMiddleware auth,
|
||||||
@ -24,55 +26,55 @@ genAuth wasp = case maybeAuth of
|
|||||||
]
|
]
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
where
|
where
|
||||||
maybeAuth = getAuth wasp
|
maybeAuth = AS.App.auth $ snd $ AS.getApp spec
|
||||||
|
|
||||||
-- | Generates core/auth file which contains auth middleware and createUser() function.
|
-- | Generates core/auth file which contains auth middleware and createUser() function.
|
||||||
genCoreAuth :: Wasp.Auth.Auth -> FileDraft
|
genCoreAuth :: AS.Auth.Auth -> FileDraft
|
||||||
genCoreAuth auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genCoreAuth auth = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
coreAuthRelToSrc = [relfile|core/auth.js|]
|
coreAuthRelToSrc = [relfile|core/auth.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> coreAuthRelToSrc
|
tmplFile = C.asTmplFile $ [reldir|src|] </> coreAuthRelToSrc
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile coreAuthRelToSrc
|
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile coreAuthRelToSrc
|
||||||
|
|
||||||
tmplData =
|
tmplData =
|
||||||
let userEntity = Wasp.Auth._userEntity auth
|
let userEntityName = AS.refName $ AS.Auth.userEntity auth
|
||||||
in object
|
in object
|
||||||
[ "userEntityUpper" .= userEntity,
|
[ "userEntityUpper" .= (userEntityName :: String),
|
||||||
"userEntityLower" .= Util.toLowerFirst userEntity
|
"userEntityLower" .= (Util.toLowerFirst userEntityName :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
genAuthMiddleware :: Wasp.Auth.Auth -> FileDraft
|
genAuthMiddleware :: AS.Auth.Auth -> FileDraft
|
||||||
genAuthMiddleware auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genAuthMiddleware auth = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
authMiddlewareRelToSrc = [relfile|core/auth/prismaMiddleware.js|]
|
authMiddlewareRelToSrc = [relfile|core/auth/prismaMiddleware.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> authMiddlewareRelToSrc
|
tmplFile = C.asTmplFile $ [reldir|src|] </> authMiddlewareRelToSrc
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile authMiddlewareRelToSrc
|
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile authMiddlewareRelToSrc
|
||||||
|
|
||||||
tmplData =
|
tmplData =
|
||||||
let userEntity = Wasp.Auth._userEntity auth
|
let userEntityName = AS.refName $ AS.Auth.userEntity auth
|
||||||
in object
|
in object
|
||||||
[ "userEntityUpper" .= userEntity
|
[ "userEntityUpper" .= (userEntityName :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
genAuthRoutesIndex :: FileDraft
|
genAuthRoutesIndex :: FileDraft
|
||||||
genAuthRoutesIndex = C.copySrcTmplAsIs (C.asTmplSrcFile [relfile|routes/auth/index.js|])
|
genAuthRoutesIndex = C.mkSrcTmplFd (C.asTmplSrcFile [relfile|routes/auth/index.js|])
|
||||||
|
|
||||||
genLoginRoute :: Wasp.Auth.Auth -> FileDraft
|
genLoginRoute :: AS.Auth.Auth -> FileDraft
|
||||||
genLoginRoute auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genLoginRoute auth = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
loginRouteRelToSrc = [relfile|routes/auth/login.js|]
|
loginRouteRelToSrc = [relfile|routes/auth/login.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> loginRouteRelToSrc
|
tmplFile = C.asTmplFile $ [reldir|src|] </> loginRouteRelToSrc
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile loginRouteRelToSrc
|
dstFile = C.serverSrcDirInServerRootDir </> C.asServerSrcFile loginRouteRelToSrc
|
||||||
|
|
||||||
tmplData =
|
tmplData =
|
||||||
let userEntity = Wasp.Auth._userEntity auth
|
let userEntityName = AS.refName $ AS.Auth.userEntity auth
|
||||||
in object
|
in object
|
||||||
[ "userEntityUpper" .= userEntity,
|
[ "userEntityUpper" .= (userEntityName :: String),
|
||||||
"userEntityLower" .= Util.toLowerFirst userEntity
|
"userEntityLower" .= (Util.toLowerFirst userEntityName :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
genSignupRoute :: Wasp.Auth.Auth -> FileDraft
|
genSignupRoute :: AS.Auth.Auth -> FileDraft
|
||||||
genSignupRoute auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genSignupRoute auth = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
signupRouteRelToSrc = [relfile|routes/auth/signup.js|]
|
signupRouteRelToSrc = [relfile|routes/auth/signup.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> signupRouteRelToSrc
|
tmplFile = C.asTmplFile $ [reldir|src|] </> signupRouteRelToSrc
|
||||||
@ -80,11 +82,11 @@ genSignupRoute auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
|||||||
|
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "userEntityLower" .= Util.toLowerFirst (Wasp.Auth._userEntity auth)
|
[ "userEntityLower" .= (Util.toLowerFirst (AS.refName $ AS.Auth.userEntity auth) :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
genMeRoute :: Wasp.Auth.Auth -> FileDraft
|
genMeRoute :: AS.Auth.Auth -> FileDraft
|
||||||
genMeRoute auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genMeRoute auth = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
meRouteRelToSrc = [relfile|routes/auth/me.js|]
|
meRouteRelToSrc = [relfile|routes/auth/me.js|]
|
||||||
tmplFile = C.asTmplFile $ [reldir|src|] </> meRouteRelToSrc
|
tmplFile = C.asTmplFile $ [reldir|src|] </> meRouteRelToSrc
|
||||||
@ -92,5 +94,5 @@ genMeRoute auth = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
|||||||
|
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "userEntityLower" .= Util.toLowerFirst (Wasp.Auth._userEntity auth)
|
[ "userEntityLower" .= (Util.toLowerFirst (AS.refName $ AS.Auth.userEntity auth) :: String)
|
||||||
]
|
]
|
||||||
|
@ -2,10 +2,9 @@ module Wasp.Generator.ServerGenerator.Common
|
|||||||
( serverRootDirInProjectRootDir,
|
( serverRootDirInProjectRootDir,
|
||||||
serverSrcDirInServerRootDir,
|
serverSrcDirInServerRootDir,
|
||||||
serverSrcDirInProjectRootDir,
|
serverSrcDirInProjectRootDir,
|
||||||
copyTmplAsIs,
|
mkTmplFd,
|
||||||
makeSimpleTemplateFD,
|
mkTmplFdWithDstAndData,
|
||||||
makeTemplateFD,
|
mkSrcTmplFd,
|
||||||
copySrcTmplAsIs,
|
|
||||||
srcDirInServerTemplatesDir,
|
srcDirInServerTemplatesDir,
|
||||||
asTmplFile,
|
asTmplFile,
|
||||||
asTmplSrcFile,
|
asTmplSrcFile,
|
||||||
@ -24,7 +23,6 @@ import qualified StrongPath as SP
|
|||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
||||||
import Wasp.Generator.Templates (TemplatesDir)
|
import Wasp.Generator.Templates (TemplatesDir)
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
|
|
||||||
data ServerRootDir
|
data ServerRootDir
|
||||||
|
|
||||||
@ -61,29 +59,24 @@ serverSrcDirInProjectRootDir = serverRootDirInProjectRootDir </> serverSrcDirInS
|
|||||||
|
|
||||||
-- * Templates
|
-- * Templates
|
||||||
|
|
||||||
copyTmplAsIs :: Path' (Rel ServerTemplatesDir) File' -> FileDraft
|
mkTmplFd :: Path' (Rel ServerTemplatesDir) File' -> FileDraft
|
||||||
copyTmplAsIs srcPath = makeTemplateFD srcPath dstPath Nothing
|
mkTmplFd srcPath = mkTmplFdWithDstAndData srcPath dstPath Nothing
|
||||||
where
|
where
|
||||||
dstPath = SP.castRel srcPath :: Path' (Rel ServerRootDir) File'
|
dstPath = SP.castRel srcPath :: Path' (Rel ServerRootDir) File'
|
||||||
|
|
||||||
makeSimpleTemplateFD :: Path' (Rel ServerTemplatesDir) File' -> Wasp -> FileDraft
|
mkTmplFdWithDstAndData ::
|
||||||
makeSimpleTemplateFD srcPath wasp = makeTemplateFD srcPath dstPath (Just $ Aeson.toJSON wasp)
|
|
||||||
where
|
|
||||||
dstPath = SP.castRel srcPath :: Path' (Rel ServerRootDir) File'
|
|
||||||
|
|
||||||
makeTemplateFD ::
|
|
||||||
Path' (Rel ServerTemplatesDir) File' ->
|
Path' (Rel ServerTemplatesDir) File' ->
|
||||||
Path' (Rel ServerRootDir) File' ->
|
Path' (Rel ServerRootDir) File' ->
|
||||||
Maybe Aeson.Value ->
|
Maybe Aeson.Value ->
|
||||||
FileDraft
|
FileDraft
|
||||||
makeTemplateFD relSrcPath relDstPath tmplData =
|
mkTmplFdWithDstAndData relSrcPath relDstPath tmplData =
|
||||||
createTemplateFileDraft
|
createTemplateFileDraft
|
||||||
(serverRootDirInProjectRootDir </> relDstPath)
|
(serverRootDirInProjectRootDir </> relDstPath)
|
||||||
(serverTemplatesDirInTemplatesDir </> relSrcPath)
|
(serverTemplatesDirInTemplatesDir </> relSrcPath)
|
||||||
tmplData
|
tmplData
|
||||||
|
|
||||||
copySrcTmplAsIs :: Path' (Rel ServerTemplatesSrcDir) File' -> FileDraft
|
mkSrcTmplFd :: Path' (Rel ServerTemplatesSrcDir) File' -> FileDraft
|
||||||
copySrcTmplAsIs pathInTemplatesSrcDir = makeTemplateFD srcPath dstPath Nothing
|
mkSrcTmplFd pathInTemplatesSrcDir = mkTmplFdWithDstAndData srcPath dstPath Nothing
|
||||||
where
|
where
|
||||||
srcPath = srcDirInServerTemplatesDir </> pathInTemplatesSrcDir
|
srcPath = srcDirInServerTemplatesDir </> pathInTemplatesSrcDir
|
||||||
dstPath =
|
dstPath =
|
||||||
|
@ -5,21 +5,21 @@ module Wasp.Generator.ServerGenerator.ConfigG
|
|||||||
where
|
where
|
||||||
|
|
||||||
import Data.Aeson (object, (.=))
|
import Data.Aeson (object, (.=))
|
||||||
import Data.Maybe (isJust)
|
|
||||||
import StrongPath (File', Path', Rel, relfile, (</>))
|
import StrongPath (File', Path', Rel, relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||||
import Wasp.Wasp (Wasp, getAuth)
|
|
||||||
|
|
||||||
genConfigFile :: Wasp -> FileDraft
|
genConfigFile :: AppSpec -> FileDraft
|
||||||
genConfigFile wasp = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genConfigFile spec = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
tmplFile = C.srcDirInServerTemplatesDir </> SP.castRel configFileInSrcDir
|
tmplFile = C.srcDirInServerTemplatesDir </> SP.castRel configFileInSrcDir
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> configFileInSrcDir
|
dstFile = C.serverSrcDirInServerRootDir </> configFileInSrcDir
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "isAuthEnabled" .= isJust (getAuth wasp)
|
[ "isAuthEnabled" .= (AS.isAuthEnabled spec :: Bool)
|
||||||
]
|
]
|
||||||
|
|
||||||
configFileInSrcDir :: Path' (Rel C.ServerSrcDir) File'
|
configFileInSrcDir :: Path' (Rel C.ServerSrcDir) File'
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.ServerGenerator.OperationsG
|
module Wasp.Generator.ServerGenerator.OperationsG
|
||||||
( genOperations,
|
( genOperations,
|
||||||
queryFileInSrcDir,
|
queryFileInSrcDir,
|
||||||
@ -12,80 +14,76 @@ import Data.Char (toLower)
|
|||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import StrongPath (Dir, Dir', File', Path, Path', Posix, Rel, reldir, reldirP, relfile, (</>))
|
import StrongPath (Dir, Dir', File', Path, Path', Posix, Rel, reldir, reldirP, relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.Action as AS.Action
|
||||||
|
import qualified Wasp.AppSpec.Operation as AS.Operation
|
||||||
|
import qualified Wasp.AppSpec.Query as AS.Query
|
||||||
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
import Wasp.Generator.ExternalCodeGenerator.Common (GeneratedExternalCodeDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import Wasp.Generator.JsImport (getImportDetailsForJsFnImport)
|
import Wasp.Generator.JsImport (getJsImportDetailsForExtFnImport)
|
||||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.Action as Wasp.Action
|
|
||||||
import qualified Wasp.Wasp.Operation as Wasp.Operation
|
|
||||||
import qualified Wasp.Wasp.Query as Wasp.Query
|
|
||||||
|
|
||||||
genOperations :: Wasp -> [FileDraft]
|
genOperations :: AppSpec -> [FileDraft]
|
||||||
genOperations wasp =
|
genOperations spec = genQueries spec ++ genActions spec
|
||||||
genQueries wasp
|
|
||||||
++ genActions wasp
|
|
||||||
|
|
||||||
genQueries :: Wasp -> [FileDraft]
|
genQueries :: AppSpec -> [FileDraft]
|
||||||
genQueries wasp =
|
genQueries spec = map (genQuery spec) (AS.getQueries spec)
|
||||||
map (genQuery wasp) (Wasp.getQueries wasp)
|
|
||||||
|
|
||||||
genActions :: Wasp -> [FileDraft]
|
genActions :: AppSpec -> [FileDraft]
|
||||||
genActions wasp =
|
genActions spec = map (genAction spec) (AS.getActions spec)
|
||||||
map (genAction wasp) (Wasp.getActions wasp)
|
|
||||||
|
|
||||||
-- | Here we generate JS file that basically imports JS query function provided by user,
|
-- | Here we generate JS file that basically imports JS query function provided by user,
|
||||||
-- decorates it (mostly injects stuff into it) and exports. Idea is that the rest of the server,
|
-- decorates it (mostly injects stuff into it) and exports. Idea is that the rest of the server,
|
||||||
-- and user also, should use this new JS function, and not the old one directly.
|
-- and user also, should use this new JS function, and not the old one directly.
|
||||||
genQuery :: Wasp -> Wasp.Query.Query -> FileDraft
|
genQuery :: AppSpec -> (String, AS.Query.Query) -> FileDraft
|
||||||
genQuery _ query = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genQuery _ (queryName, query) = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
operation = Wasp.Operation.QueryOp query
|
operation = AS.Operation.QueryOp queryName query
|
||||||
tmplFile = C.asTmplFile [relfile|src/queries/_query.js|]
|
tmplFile = C.asTmplFile [relfile|src/queries/_query.js|]
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> queryFileInSrcDir query
|
dstFile = C.serverSrcDirInServerRootDir </> queryFileInSrcDir queryName
|
||||||
tmplData = operationTmplData operation
|
tmplData = operationTmplData operation
|
||||||
|
|
||||||
-- | Analogous to genQuery.
|
-- | Analogous to genQuery.
|
||||||
genAction :: Wasp -> Wasp.Action.Action -> FileDraft
|
genAction :: AppSpec -> (String, AS.Action.Action) -> FileDraft
|
||||||
genAction _ action = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genAction _ (actionName, action) = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
operation = Wasp.Operation.ActionOp action
|
operation = AS.Operation.ActionOp actionName action
|
||||||
tmplFile = [relfile|src/actions/_action.js|]
|
tmplFile = [relfile|src/actions/_action.js|]
|
||||||
dstFile = C.serverSrcDirInServerRootDir </> actionFileInSrcDir action
|
dstFile = C.serverSrcDirInServerRootDir </> actionFileInSrcDir actionName
|
||||||
tmplData = operationTmplData operation
|
tmplData = operationTmplData operation
|
||||||
|
|
||||||
queryFileInSrcDir :: Wasp.Query.Query -> Path' (Rel C.ServerSrcDir) File'
|
queryFileInSrcDir :: String -> Path' (Rel C.ServerSrcDir) File'
|
||||||
queryFileInSrcDir query =
|
queryFileInSrcDir queryName =
|
||||||
[reldir|queries|]
|
[reldir|queries|]
|
||||||
-- TODO: fromJust here could fail if there is some problem with the name, we should handle this.
|
-- TODO: fromJust here could fail if there is some problem with the name, we should handle this.
|
||||||
</> fromJust (SP.parseRelFile $ Wasp.Query._name query ++ ".js")
|
</> fromJust (SP.parseRelFile $ queryName ++ ".js")
|
||||||
|
|
||||||
actionFileInSrcDir :: Wasp.Action.Action -> Path' (Rel C.ServerSrcDir) File'
|
actionFileInSrcDir :: String -> Path' (Rel C.ServerSrcDir) File'
|
||||||
actionFileInSrcDir action =
|
actionFileInSrcDir actionName =
|
||||||
[reldir|actions|]
|
[reldir|actions|]
|
||||||
-- TODO: fromJust here could fail if there is some problem with the name, we should handle this.
|
-- TODO: fromJust here could fail if there is some problem with the name, we should handle this.
|
||||||
</> fromJust (SP.parseRelFile $ Wasp.Action._name action ++ ".js")
|
</> fromJust (SP.parseRelFile $ actionName ++ ".js")
|
||||||
|
|
||||||
operationFileInSrcDir :: Wasp.Operation.Operation -> Path' (Rel C.ServerSrcDir) File'
|
operationFileInSrcDir :: AS.Operation.Operation -> Path' (Rel C.ServerSrcDir) File'
|
||||||
operationFileInSrcDir (Wasp.Operation.QueryOp query) = queryFileInSrcDir query
|
operationFileInSrcDir (AS.Operation.QueryOp name _) = queryFileInSrcDir name
|
||||||
operationFileInSrcDir (Wasp.Operation.ActionOp action) = actionFileInSrcDir action
|
operationFileInSrcDir (AS.Operation.ActionOp name _) = actionFileInSrcDir name
|
||||||
|
|
||||||
-- | TODO: Make this not hardcoded!
|
-- | TODO: Make this not hardcoded!
|
||||||
relPosixPathFromOperationFileToExtSrcDir :: Path Posix (Rel Dir') (Dir GeneratedExternalCodeDir)
|
relPosixPathFromOperationFileToExtSrcDir :: Path Posix (Rel Dir') (Dir GeneratedExternalCodeDir)
|
||||||
relPosixPathFromOperationFileToExtSrcDir = [reldirP|../ext-src/|]
|
relPosixPathFromOperationFileToExtSrcDir = [reldirP|../ext-src/|]
|
||||||
|
|
||||||
operationTmplData :: Wasp.Operation.Operation -> Aeson.Value
|
operationTmplData :: AS.Operation.Operation -> Aeson.Value
|
||||||
operationTmplData operation =
|
operationTmplData operation =
|
||||||
object
|
object
|
||||||
[ "jsFnImportStatement" .= importStmt,
|
[ "jsFnImportStatement" .= importStmt,
|
||||||
"jsFnIdentifier" .= importIdentifier,
|
"jsFnIdentifier" .= importIdentifier,
|
||||||
"entities" .= maybe [] (map buildEntityData) (Wasp.Operation.getEntities operation)
|
"entities" .= maybe [] (map (buildEntityData . AS.refName)) (AS.Operation.getEntities operation)
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
(importIdentifier, importStmt) =
|
(importIdentifier, importStmt) =
|
||||||
getImportDetailsForJsFnImport relPosixPathFromOperationFileToExtSrcDir $
|
getJsImportDetailsForExtFnImport relPosixPathFromOperationFileToExtSrcDir $
|
||||||
Wasp.Operation.getJsFn operation
|
AS.Operation.getFn operation
|
||||||
buildEntityData :: String -> Aeson.Value
|
buildEntityData :: String -> Aeson.Value
|
||||||
buildEntityData entityName =
|
buildEntityData entityName =
|
||||||
object
|
object
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.ServerGenerator.OperationsRoutesG
|
module Wasp.Generator.ServerGenerator.OperationsRoutesG
|
||||||
( genOperationsRoutes,
|
( genOperationsRoutes,
|
||||||
operationRouteInOperationsRouter,
|
operationRouteInOperationsRouter,
|
||||||
@ -9,54 +11,55 @@ import qualified Data.Aeson as Aeson
|
|||||||
import Data.Maybe (fromJust, fromMaybe, isJust)
|
import Data.Maybe (fromJust, fromMaybe, isJust)
|
||||||
import StrongPath (Dir, File', Path, Path', Posix, Rel, reldir, reldirP, relfile, (</>))
|
import StrongPath (Dir, File', Path, Path', Posix, Rel, reldir, reldirP, relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
|
import Wasp.AppSpec (AppSpec, getApp)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.Action as AS.Action
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Auth as AS.Auth
|
||||||
|
import qualified Wasp.AppSpec.Operation as AS.Operation
|
||||||
|
import qualified Wasp.AppSpec.Query as AS.Query
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import qualified Wasp.Generator.ServerGenerator.Common as C
|
import qualified Wasp.Generator.ServerGenerator.Common as C
|
||||||
import Wasp.Generator.ServerGenerator.OperationsG (operationFileInSrcDir)
|
import Wasp.Generator.ServerGenerator.OperationsG (operationFileInSrcDir)
|
||||||
import qualified Wasp.Util as U
|
import qualified Wasp.Util as U
|
||||||
import Wasp.Wasp (Wasp, getAuth)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.Action as Wasp.Action
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
import qualified Wasp.Wasp.Operation as Wasp.Operation
|
|
||||||
import qualified Wasp.Wasp.Query as Wasp.Query
|
|
||||||
|
|
||||||
genOperationsRoutes :: Wasp -> [FileDraft]
|
genOperationsRoutes :: AppSpec -> [FileDraft]
|
||||||
genOperationsRoutes wasp =
|
genOperationsRoutes spec =
|
||||||
concat
|
concat
|
||||||
[ map (genActionRoute wasp) (Wasp.getActions wasp),
|
[ map (genActionRoute spec) (AS.getActions spec),
|
||||||
map (genQueryRoute wasp) (Wasp.getQueries wasp),
|
map (genQueryRoute spec) (AS.getQueries spec),
|
||||||
[genOperationsRouter wasp]
|
[genOperationsRouter spec]
|
||||||
]
|
]
|
||||||
|
|
||||||
genActionRoute :: Wasp -> Wasp.Action.Action -> FileDraft
|
genActionRoute :: AppSpec -> (String, AS.Action.Action) -> FileDraft
|
||||||
genActionRoute wasp action = genOperationRoute wasp op tmplFile
|
genActionRoute spec (actionName, action) = genOperationRoute spec op tmplFile
|
||||||
where
|
where
|
||||||
op = Wasp.Operation.ActionOp action
|
op = AS.Operation.ActionOp actionName action
|
||||||
tmplFile = C.asTmplFile [relfile|src/routes/operations/_action.js|]
|
tmplFile = C.asTmplFile [relfile|src/routes/operations/_action.js|]
|
||||||
|
|
||||||
genQueryRoute :: Wasp -> Wasp.Query.Query -> FileDraft
|
genQueryRoute :: AppSpec -> (String, AS.Query.Query) -> FileDraft
|
||||||
genQueryRoute wasp query = genOperationRoute wasp op tmplFile
|
genQueryRoute spec (queryName, query) = genOperationRoute spec op tmplFile
|
||||||
where
|
where
|
||||||
op = Wasp.Operation.QueryOp query
|
op = AS.Operation.QueryOp queryName query
|
||||||
tmplFile = C.asTmplFile [relfile|src/routes/operations/_query.js|]
|
tmplFile = C.asTmplFile [relfile|src/routes/operations/_query.js|]
|
||||||
|
|
||||||
genOperationRoute :: Wasp -> Wasp.Operation.Operation -> Path' (Rel C.ServerTemplatesDir) File' -> FileDraft
|
genOperationRoute :: AppSpec -> AS.Operation.Operation -> Path' (Rel C.ServerTemplatesDir) File' -> FileDraft
|
||||||
genOperationRoute wasp operation tmplFile = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genOperationRoute spec operation tmplFile = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
dstFile = operationsRoutesDirInServerRootDir </> operationRouteFileInOperationsRoutesDir operation
|
dstFile = operationsRoutesDirInServerRootDir </> operationRouteFileInOperationsRoutesDir operation
|
||||||
|
|
||||||
baseTmplData =
|
baseTmplData =
|
||||||
object
|
object
|
||||||
[ "operationImportPath" .= operationImportPath,
|
[ "operationImportPath" .= (operationImportPath :: FilePath),
|
||||||
"operationName" .= Wasp.Operation.getName operation
|
"operationName" .= (AS.Operation.getName operation :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
tmplData = case Wasp.getAuth wasp of
|
tmplData = case AS.App.auth (snd $ getApp spec) of
|
||||||
Nothing -> baseTmplData
|
Nothing -> baseTmplData
|
||||||
Just auth ->
|
Just auth ->
|
||||||
U.jsonSet
|
U.jsonSet
|
||||||
"userEntityLower"
|
"userEntityLower"
|
||||||
(Aeson.toJSON (U.toLowerFirst $ Wasp.Auth._userEntity auth))
|
(Aeson.toJSON (U.toLowerFirst $ AS.refName $ AS.Auth.userEntity auth))
|
||||||
baseTmplData
|
baseTmplData
|
||||||
|
|
||||||
operationImportPath =
|
operationImportPath =
|
||||||
@ -72,40 +75,45 @@ operationsRoutesDirInServerSrcDir = [reldir|routes/operations/|]
|
|||||||
operationsRoutesDirInServerRootDir :: Path' (Rel C.ServerRootDir) (Dir OperationsRoutesDir)
|
operationsRoutesDirInServerRootDir :: Path' (Rel C.ServerRootDir) (Dir OperationsRoutesDir)
|
||||||
operationsRoutesDirInServerRootDir = C.serverSrcDirInServerRootDir </> operationsRoutesDirInServerSrcDir
|
operationsRoutesDirInServerRootDir = C.serverSrcDirInServerRootDir </> operationsRoutesDirInServerSrcDir
|
||||||
|
|
||||||
operationRouteFileInOperationsRoutesDir :: Wasp.Operation.Operation -> Path' (Rel OperationsRoutesDir) File'
|
operationRouteFileInOperationsRoutesDir :: AS.Operation.Operation -> Path' (Rel OperationsRoutesDir) File'
|
||||||
operationRouteFileInOperationsRoutesDir operation = fromJust $ SP.parseRelFile $ Wasp.Operation.getName operation ++ ".js"
|
operationRouteFileInOperationsRoutesDir operation = fromJust $ SP.parseRelFile $ AS.Operation.getName operation ++ ".js"
|
||||||
|
|
||||||
relPosixPathFromOperationsRoutesDirToSrcDir :: Path Posix (Rel OperationsRoutesDir) (Dir C.ServerSrcDir)
|
relPosixPathFromOperationsRoutesDirToSrcDir :: Path Posix (Rel OperationsRoutesDir) (Dir C.ServerSrcDir)
|
||||||
relPosixPathFromOperationsRoutesDirToSrcDir = [reldirP|../..|]
|
relPosixPathFromOperationsRoutesDirToSrcDir = [reldirP|../..|]
|
||||||
|
|
||||||
genOperationsRouter :: Wasp -> FileDraft
|
genOperationsRouter :: AppSpec -> FileDraft
|
||||||
genOperationsRouter wasp
|
genOperationsRouter spec
|
||||||
-- TODO: Right now we are throwing error here, but we should instead perform this check in parsing/analyzer phase, as a semantic check, since we have all the info we need then already.
|
-- TODO: Right now we are throwing error here, but we should instead perform this check in parsing/analyzer phase, as a semantic check, since we have all the info we need then already.
|
||||||
| any isAuthSpecifiedForOperation operations && not isAuthEnabledGlobally = error "`auth` cannot be specified for specific operations if it is not enabled for the whole app!"
|
| any isAuthSpecifiedForOperation operations && not isAuthEnabledGlobally = error "`auth` cannot be specified for specific operations if it is not enabled for the whole app!"
|
||||||
| otherwise = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
| otherwise = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
tmplFile = C.asTmplFile [relfile|src/routes/operations/index.js|]
|
tmplFile = C.asTmplFile [relfile|src/routes/operations/index.js|]
|
||||||
dstFile = operationsRoutesDirInServerRootDir </> [relfile|index.js|]
|
dstFile = operationsRoutesDirInServerRootDir </> [relfile|index.js|]
|
||||||
operations =
|
operations =
|
||||||
map Wasp.Operation.ActionOp (Wasp.getActions wasp)
|
map (uncurry AS.Operation.ActionOp) (AS.getActions spec)
|
||||||
++ map Wasp.Operation.QueryOp (Wasp.getQueries wasp)
|
++ map (uncurry AS.Operation.QueryOp) (AS.getQueries spec)
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "operationRoutes" .= map makeOperationRoute operations,
|
[ "operationRoutes" .= map makeOperationRoute operations,
|
||||||
"isAuthEnabled" .= isAuthEnabledGlobally
|
"isAuthEnabled" .= isAuthEnabledGlobally
|
||||||
]
|
]
|
||||||
makeOperationRoute operation =
|
makeOperationRoute operation =
|
||||||
let operationName = Wasp.Operation.getName operation
|
let operationName = AS.Operation.getName operation
|
||||||
in object
|
in object
|
||||||
[ "importIdentifier" .= operationName,
|
[ "importIdentifier" .= operationName,
|
||||||
"importPath" .= ("./" ++ SP.fromRelFileP (fromJust $ SP.relFileToPosix $ operationRouteFileInOperationsRoutesDir operation)),
|
"importPath"
|
||||||
|
.= ( "./"
|
||||||
|
++ SP.fromRelFileP
|
||||||
|
( fromJust $ SP.relFileToPosix $ operationRouteFileInOperationsRoutesDir operation
|
||||||
|
)
|
||||||
|
),
|
||||||
"routePath" .= ("/" ++ operationRouteInOperationsRouter operation),
|
"routePath" .= ("/" ++ operationRouteInOperationsRouter operation),
|
||||||
"isUsingAuth" .= isAuthEnabledForOperation operation
|
"isUsingAuth" .= isAuthEnabledForOperation operation
|
||||||
]
|
]
|
||||||
|
|
||||||
isAuthEnabledGlobally = isJust $ getAuth wasp
|
isAuthEnabledGlobally = AS.isAuthEnabled spec
|
||||||
isAuthEnabledForOperation operation = fromMaybe isAuthEnabledGlobally (Wasp.Operation.getAuth operation)
|
isAuthEnabledForOperation operation = fromMaybe isAuthEnabledGlobally (AS.Operation.getAuth operation)
|
||||||
isAuthSpecifiedForOperation operation = isJust $ Wasp.Operation.getAuth operation
|
isAuthSpecifiedForOperation operation = isJust $ AS.Operation.getAuth operation
|
||||||
|
|
||||||
operationRouteInOperationsRouter :: Wasp.Operation.Operation -> String
|
operationRouteInOperationsRouter :: AS.Operation.Operation -> String
|
||||||
operationRouteInOperationsRouter = U.camelToKebabCase . Wasp.Operation.getName
|
operationRouteInOperationsRouter = U.camelToKebabCase . AS.Operation.getName
|
||||||
|
@ -4,12 +4,9 @@ module Wasp.Generator.WebAppGenerator
|
|||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
import Data.Aeson
|
import Data.Aeson (object, (.=))
|
||||||
( ToJSON (..),
|
|
||||||
object,
|
|
||||||
(.=),
|
|
||||||
)
|
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
import StrongPath
|
import StrongPath
|
||||||
( Dir,
|
( Dir,
|
||||||
Path',
|
Path',
|
||||||
@ -18,7 +15,10 @@ import StrongPath
|
|||||||
relfile,
|
relfile,
|
||||||
(</>),
|
(</>),
|
||||||
)
|
)
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
import Wasp.AppSpec (AppSpec, getApp)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Dependency as AS.Dependency
|
||||||
import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
import Wasp.Generator.ExternalCodeGenerator (generateExternalCodeDir)
|
||||||
import Wasp.Generator.FileDraft
|
import Wasp.Generator.FileDraft
|
||||||
import Wasp.Generator.PackageJsonGenerator
|
import Wasp.Generator.PackageJsonGenerator
|
||||||
@ -35,34 +35,30 @@ import qualified Wasp.Generator.WebAppGenerator.Common as C
|
|||||||
import qualified Wasp.Generator.WebAppGenerator.ExternalCodeGenerator as WebAppExternalCodeGenerator
|
import qualified Wasp.Generator.WebAppGenerator.ExternalCodeGenerator as WebAppExternalCodeGenerator
|
||||||
import Wasp.Generator.WebAppGenerator.OperationsGenerator (genOperations)
|
import Wasp.Generator.WebAppGenerator.OperationsGenerator (genOperations)
|
||||||
import qualified Wasp.Generator.WebAppGenerator.RouterGenerator as RouterGenerator
|
import qualified Wasp.Generator.WebAppGenerator.RouterGenerator as RouterGenerator
|
||||||
import qualified Wasp.NpmDependency as ND
|
|
||||||
import Wasp.Wasp
|
|
||||||
import qualified Wasp.Wasp.App as Wasp.App
|
|
||||||
import qualified Wasp.Wasp.NpmDependencies as WND
|
|
||||||
|
|
||||||
generateWebApp :: Wasp -> CompileOptions -> [FileDraft]
|
generateWebApp :: AppSpec -> [FileDraft]
|
||||||
generateWebApp wasp _ =
|
generateWebApp spec =
|
||||||
concat
|
concat
|
||||||
[ [generateReadme wasp],
|
[ [generateReadme],
|
||||||
[genPackageJson wasp waspNpmDeps],
|
[genPackageJson spec waspNpmDeps],
|
||||||
[generateGitignore wasp],
|
[generateGitignore],
|
||||||
generatePublicDir wasp,
|
generatePublicDir spec,
|
||||||
generateSrcDir wasp,
|
generateSrcDir spec,
|
||||||
generateExternalCodeDir WebAppExternalCodeGenerator.generatorStrategy wasp,
|
generateExternalCodeDir WebAppExternalCodeGenerator.generatorStrategy (AS.externalCodeFiles spec),
|
||||||
[C.makeSimpleTemplateFD (asTmplFile [relfile|netlify.toml|]) wasp]
|
[C.mkTmplFd $ asTmplFile [relfile|netlify.toml|]]
|
||||||
]
|
]
|
||||||
|
|
||||||
generateReadme :: Wasp -> FileDraft
|
generateReadme :: FileDraft
|
||||||
generateReadme wasp = C.makeSimpleTemplateFD (asTmplFile [relfile|README.md|]) wasp
|
generateReadme = C.mkTmplFd $ asTmplFile [relfile|README.md|]
|
||||||
|
|
||||||
genPackageJson :: Wasp -> [ND.NpmDependency] -> FileDraft
|
genPackageJson :: AppSpec -> [AS.Dependency.Dependency] -> FileDraft
|
||||||
genPackageJson wasp waspDeps =
|
genPackageJson spec waspDeps =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(C.asTmplFile [relfile|package.json|])
|
(C.asTmplFile [relfile|package.json|])
|
||||||
(C.asWebAppFile [relfile|package.json|])
|
(C.asWebAppFile [relfile|package.json|])
|
||||||
( Just $
|
( Just $
|
||||||
object
|
object
|
||||||
[ "wasp" .= wasp,
|
[ "appName" .= (fst (getApp spec) :: String),
|
||||||
"depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps)
|
"depsChunk" .= npmDepsToPackageJsonEntry (resolvedWaspDeps ++ resolvedUserDeps)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -72,12 +68,12 @@ genPackageJson wasp waspDeps =
|
|||||||
Right deps -> deps
|
Right deps -> deps
|
||||||
Left depsAndErrors -> error $ intercalate " ; " $ map snd depsAndErrors
|
Left depsAndErrors -> error $ intercalate " ; " $ map snd depsAndErrors
|
||||||
|
|
||||||
userDeps :: [ND.NpmDependency]
|
userDeps :: [AS.Dependency.Dependency]
|
||||||
userDeps = WND._dependencies $ Wasp.Wasp.getNpmDependencies wasp
|
userDeps = fromMaybe [] $ AS.App.dependencies $ snd $ getApp spec
|
||||||
|
|
||||||
waspNpmDeps :: [ND.NpmDependency]
|
waspNpmDeps :: [AS.Dependency.Dependency]
|
||||||
waspNpmDeps =
|
waspNpmDeps =
|
||||||
ND.fromList
|
AS.Dependency.fromList
|
||||||
[ ("axios", "^0.21.1"),
|
[ ("axios", "^0.21.1"),
|
||||||
("lodash", "^4.17.15"),
|
("lodash", "^4.17.15"),
|
||||||
("react", "^16.12.0"),
|
("react", "^16.12.0"),
|
||||||
@ -90,25 +86,26 @@ waspNpmDeps =
|
|||||||
|
|
||||||
-- TODO: Also extract devDependencies like we did dependencies (waspNpmDeps).
|
-- TODO: Also extract devDependencies like we did dependencies (waspNpmDeps).
|
||||||
|
|
||||||
generateGitignore :: Wasp -> FileDraft
|
generateGitignore :: FileDraft
|
||||||
generateGitignore wasp =
|
generateGitignore =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDst
|
||||||
(asTmplFile [relfile|gitignore|])
|
(asTmplFile [relfile|gitignore|])
|
||||||
(asWebAppFile [relfile|.gitignore|])
|
(asWebAppFile [relfile|.gitignore|])
|
||||||
(Just $ toJSON wasp)
|
|
||||||
|
|
||||||
generatePublicDir :: Wasp -> [FileDraft]
|
generatePublicDir :: AppSpec -> [FileDraft]
|
||||||
generatePublicDir wasp =
|
generatePublicDir spec =
|
||||||
C.copyTmplAsIs (asTmplFile [relfile|public/favicon.ico|]) :
|
C.mkTmplFd (asTmplFile [relfile|public/favicon.ico|]) :
|
||||||
generatePublicIndexHtml wasp :
|
generatePublicIndexHtml spec :
|
||||||
map
|
( let tmplData = object ["appName" .= (fst (getApp spec) :: String)]
|
||||||
(\path -> C.makeSimpleTemplateFD (asTmplFile $ [reldir|public|] </> path) wasp)
|
processPublicTmpl path = C.mkTmplFdWithData (asTmplFile $ [reldir|public|] </> path) tmplData
|
||||||
[ [relfile|manifest.json|]
|
in processPublicTmpl
|
||||||
|
<$> [ [relfile|manifest.json|]
|
||||||
]
|
]
|
||||||
|
)
|
||||||
|
|
||||||
generatePublicIndexHtml :: Wasp -> FileDraft
|
generatePublicIndexHtml :: AppSpec -> FileDraft
|
||||||
generatePublicIndexHtml wasp =
|
generatePublicIndexHtml spec =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|public/index.html|])
|
(asTmplFile [relfile|public/index.html|])
|
||||||
targetPath
|
targetPath
|
||||||
(Just templateData)
|
(Just templateData)
|
||||||
@ -116,8 +113,8 @@ generatePublicIndexHtml wasp =
|
|||||||
targetPath = [relfile|public/index.html|]
|
targetPath = [relfile|public/index.html|]
|
||||||
templateData =
|
templateData =
|
||||||
object
|
object
|
||||||
[ "title" .= Wasp.App.appTitle (getApp wasp),
|
[ "title" .= (AS.App.title (snd $ getApp spec) :: String),
|
||||||
"head" .= maybe "" (intercalate "\n") (Wasp.App.appHead $ getApp wasp)
|
"head" .= (maybe "" (intercalate "\n") (AS.App.head $ snd $ getApp spec) :: String)
|
||||||
]
|
]
|
||||||
|
|
||||||
-- * Src dir
|
-- * Src dir
|
||||||
@ -132,15 +129,15 @@ srcDir = C.webAppSrcDirInWebAppRootDir
|
|||||||
|
|
||||||
-- | Generates api.js file which contains token management and configured api (e.g. axios) instance.
|
-- | Generates api.js file which contains token management and configured api (e.g. axios) instance.
|
||||||
genApi :: FileDraft
|
genApi :: FileDraft
|
||||||
genApi = C.copyTmplAsIs (C.asTmplFile [relfile|src/api.js|])
|
genApi = C.mkTmplFd (C.asTmplFile [relfile|src/api.js|])
|
||||||
|
|
||||||
generateSrcDir :: Wasp -> [FileDraft]
|
generateSrcDir :: AppSpec -> [FileDraft]
|
||||||
generateSrcDir wasp =
|
generateSrcDir spec =
|
||||||
generateLogo :
|
generateLogo :
|
||||||
RouterGenerator.generateRouter wasp :
|
RouterGenerator.generateRouter spec :
|
||||||
genApi :
|
genApi :
|
||||||
map
|
map
|
||||||
makeSimpleSrcTemplateFD
|
processSrcTmpl
|
||||||
[ [relfile|index.js|],
|
[ [relfile|index.js|],
|
||||||
[relfile|index.css|],
|
[relfile|index.css|],
|
||||||
[relfile|serviceWorker.js|],
|
[relfile|serviceWorker.js|],
|
||||||
@ -148,16 +145,15 @@ generateSrcDir wasp =
|
|||||||
[relfile|queryCache.js|],
|
[relfile|queryCache.js|],
|
||||||
[relfile|utils.js|]
|
[relfile|utils.js|]
|
||||||
]
|
]
|
||||||
++ genOperations wasp
|
++ genOperations spec
|
||||||
++ AuthG.genAuth wasp
|
++ AuthG.genAuth spec
|
||||||
where
|
where
|
||||||
generateLogo =
|
generateLogo =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile [relfile|src/logo.png|])
|
(asTmplFile [relfile|src/logo.png|])
|
||||||
(srcDir </> asWebAppSrcFile [relfile|logo.png|])
|
(srcDir </> asWebAppSrcFile [relfile|logo.png|])
|
||||||
Nothing
|
Nothing
|
||||||
makeSimpleSrcTemplateFD path =
|
processSrcTmpl path =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDst
|
||||||
(asTmplFile $ [reldir|src|] </> path)
|
(asTmplFile $ [reldir|src|] </> path)
|
||||||
(srcDir </> asWebAppSrcFile path)
|
(srcDir </> asWebAppSrcFile path)
|
||||||
(Just $ toJSON wasp)
|
|
||||||
|
@ -5,14 +5,17 @@ where
|
|||||||
|
|
||||||
import Data.Aeson (object, (.=))
|
import Data.Aeson (object, (.=))
|
||||||
import Data.Aeson.Types (Pair)
|
import Data.Aeson.Types (Pair)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
import StrongPath (File', Path', Rel', reldir, relfile, (</>))
|
import StrongPath (File', Path', Rel', reldir, relfile, (</>))
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.App as AS.App
|
||||||
|
import qualified Wasp.AppSpec.App.Auth as AS.Auth
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import Wasp.Generator.WebAppGenerator.Common as C
|
import Wasp.Generator.WebAppGenerator.Common as C
|
||||||
import Wasp.Wasp (Wasp, getAuth)
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
|
|
||||||
genAuth :: Wasp -> [FileDraft]
|
genAuth :: AppSpec -> [FileDraft]
|
||||||
genAuth wasp = case maybeAuth of
|
genAuth spec = case maybeAuth of
|
||||||
Just auth ->
|
Just auth ->
|
||||||
[ genSignup,
|
[ genSignup,
|
||||||
genLogin,
|
genLogin,
|
||||||
@ -23,54 +26,56 @@ genAuth wasp = case maybeAuth of
|
|||||||
++ genAuthForms auth
|
++ genAuthForms auth
|
||||||
Nothing -> []
|
Nothing -> []
|
||||||
where
|
where
|
||||||
maybeAuth = getAuth wasp
|
maybeAuth = AS.App.auth $ snd $ AS.getApp spec
|
||||||
|
|
||||||
-- | Generates file with signup function to be used by Wasp developer.
|
-- | Generates file with signup function to be used by Wasp developer.
|
||||||
genSignup :: FileDraft
|
genSignup :: FileDraft
|
||||||
genSignup = C.copyTmplAsIs (C.asTmplFile [relfile|src/auth/signup.js|])
|
genSignup = C.mkTmplFd (C.asTmplFile [relfile|src/auth/signup.js|])
|
||||||
|
|
||||||
-- | Generates file with login function to be used by Wasp developer.
|
-- | Generates file with login function to be used by Wasp developer.
|
||||||
genLogin :: FileDraft
|
genLogin :: FileDraft
|
||||||
genLogin = C.copyTmplAsIs (C.asTmplFile [relfile|src/auth/login.js|])
|
genLogin = C.mkTmplFd (C.asTmplFile [relfile|src/auth/login.js|])
|
||||||
|
|
||||||
-- | Generates file with logout function to be used by Wasp developer.
|
-- | Generates file with logout function to be used by Wasp developer.
|
||||||
genLogout :: FileDraft
|
genLogout :: FileDraft
|
||||||
genLogout = C.copyTmplAsIs (C.asTmplFile [relfile|src/auth/logout.js|])
|
genLogout = C.mkTmplFd (C.asTmplFile [relfile|src/auth/logout.js|])
|
||||||
|
|
||||||
-- | Generates HOC that handles auth for the given page.
|
-- | Generates HOC that handles auth for the given page.
|
||||||
genCreateAuthRequiredPage :: Wasp.Auth.Auth -> FileDraft
|
genCreateAuthRequiredPage :: AS.Auth.Auth -> FileDraft
|
||||||
genCreateAuthRequiredPage auth =
|
genCreateAuthRequiredPage auth =
|
||||||
compileTmplToSamePath
|
compileTmplToSamePath
|
||||||
[relfile|auth/pages/createAuthRequiredPage.js|]
|
[relfile|auth/pages/createAuthRequiredPage.js|]
|
||||||
["onAuthFailedRedirectTo" .= Wasp.Auth._onAuthFailedRedirectTo auth]
|
["onAuthFailedRedirectTo" .= AS.Auth.onAuthFailedRedirectTo auth]
|
||||||
|
|
||||||
-- | Generates React hook that Wasp developer can use in a component to get
|
-- | Generates React hook that Wasp developer can use in a component to get
|
||||||
-- access to the currently logged in user (and check whether user is logged in
|
-- access to the currently logged in user (and check whether user is logged in
|
||||||
-- ot not).
|
-- ot not).
|
||||||
genUseAuth :: FileDraft
|
genUseAuth :: FileDraft
|
||||||
genUseAuth = C.copyTmplAsIs (C.asTmplFile [relfile|src/auth/useAuth.js|])
|
genUseAuth = C.mkTmplFd (C.asTmplFile [relfile|src/auth/useAuth.js|])
|
||||||
|
|
||||||
genAuthForms :: Wasp.Auth.Auth -> [FileDraft]
|
genAuthForms :: AS.Auth.Auth -> [FileDraft]
|
||||||
genAuthForms auth =
|
genAuthForms auth =
|
||||||
[ genLoginForm auth,
|
[ genLoginForm auth,
|
||||||
genSignupForm auth
|
genSignupForm auth
|
||||||
]
|
]
|
||||||
|
|
||||||
genLoginForm :: Wasp.Auth.Auth -> FileDraft
|
genLoginForm :: AS.Auth.Auth -> FileDraft
|
||||||
genLoginForm auth =
|
genLoginForm auth =
|
||||||
|
-- TODO: Logic that says "/" is a default redirect on success is duplicated here and in the function below.
|
||||||
|
-- We should remove that duplication.
|
||||||
compileTmplToSamePath
|
compileTmplToSamePath
|
||||||
[relfile|auth/forms/Login.js|]
|
[relfile|auth/forms/Login.js|]
|
||||||
["onAuthSucceededRedirectTo" .= Wasp.Auth._onAuthSucceededRedirectTo auth]
|
["onAuthSucceededRedirectTo" .= fromMaybe "/" (AS.Auth.onAuthSucceededRedirectTo auth)]
|
||||||
|
|
||||||
genSignupForm :: Wasp.Auth.Auth -> FileDraft
|
genSignupForm :: AS.Auth.Auth -> FileDraft
|
||||||
genSignupForm auth =
|
genSignupForm auth =
|
||||||
compileTmplToSamePath
|
compileTmplToSamePath
|
||||||
[relfile|auth/forms/Signup.js|]
|
[relfile|auth/forms/Signup.js|]
|
||||||
["onAuthSucceededRedirectTo" .= Wasp.Auth._onAuthSucceededRedirectTo auth]
|
["onAuthSucceededRedirectTo" .= fromMaybe "/" (AS.Auth.onAuthSucceededRedirectTo auth)]
|
||||||
|
|
||||||
compileTmplToSamePath :: Path' Rel' File' -> [Pair] -> FileDraft
|
compileTmplToSamePath :: Path' Rel' File' -> [Pair] -> FileDraft
|
||||||
compileTmplToSamePath tmplFileInTmplSrcDir keyValuePairs =
|
compileTmplToSamePath tmplFileInTmplSrcDir keyValuePairs =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile $ [reldir|src|] </> tmplFileInTmplSrcDir)
|
(asTmplFile $ [reldir|src|] </> tmplFileInTmplSrcDir)
|
||||||
targetPath
|
targetPath
|
||||||
(Just templateData)
|
(Just templateData)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
module Wasp.Generator.WebAppGenerator.Common
|
module Wasp.Generator.WebAppGenerator.Common
|
||||||
( webAppRootDirInProjectRootDir,
|
( webAppRootDirInProjectRootDir,
|
||||||
webAppSrcDirInWebAppRootDir,
|
webAppSrcDirInWebAppRootDir,
|
||||||
copyTmplAsIs,
|
mkTmplFd,
|
||||||
makeSimpleTemplateFD,
|
mkTmplFdWithDst,
|
||||||
makeTemplateFD,
|
mkTmplFdWithData,
|
||||||
|
mkTmplFdWithDstAndData,
|
||||||
webAppSrcDirInProjectRootDir,
|
webAppSrcDirInProjectRootDir,
|
||||||
webAppTemplatesDirInTemplatesDir,
|
webAppTemplatesDirInTemplatesDir,
|
||||||
asTmplFile,
|
asTmplFile,
|
||||||
@ -21,7 +22,6 @@ import qualified StrongPath as SP
|
|||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
import Wasp.Generator.FileDraft (FileDraft, createTemplateFileDraft)
|
||||||
import Wasp.Generator.Templates (TemplatesDir)
|
import Wasp.Generator.Templates (TemplatesDir)
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
|
|
||||||
data WebAppRootDir
|
data WebAppRootDir
|
||||||
|
|
||||||
@ -57,18 +57,21 @@ webAppSrcDirInProjectRootDir = webAppRootDirInProjectRootDir </> webAppSrcDirInW
|
|||||||
webAppTemplatesDirInTemplatesDir :: Path' (Rel TemplatesDir) (Dir WebAppTemplatesDir)
|
webAppTemplatesDirInTemplatesDir :: Path' (Rel TemplatesDir) (Dir WebAppTemplatesDir)
|
||||||
webAppTemplatesDirInTemplatesDir = [reldir|react-app|]
|
webAppTemplatesDirInTemplatesDir = [reldir|react-app|]
|
||||||
|
|
||||||
copyTmplAsIs :: Path' (Rel WebAppTemplatesDir) File' -> FileDraft
|
mkTmplFd :: Path' (Rel WebAppTemplatesDir) File' -> FileDraft
|
||||||
copyTmplAsIs path = makeTemplateFD path (SP.castRel path) Nothing
|
mkTmplFd path = mkTmplFdWithDst path (SP.castRel path)
|
||||||
|
|
||||||
makeSimpleTemplateFD :: Path' (Rel WebAppTemplatesDir) File' -> Wasp -> FileDraft
|
mkTmplFdWithDst :: Path' (Rel WebAppTemplatesDir) File' -> Path' (Rel WebAppRootDir) File' -> FileDraft
|
||||||
makeSimpleTemplateFD path wasp = makeTemplateFD path (SP.castRel path) (Just $ Aeson.toJSON wasp)
|
mkTmplFdWithDst src dst = mkTmplFdWithDstAndData src dst Nothing
|
||||||
|
|
||||||
makeTemplateFD ::
|
mkTmplFdWithData :: Path' (Rel WebAppTemplatesDir) File' -> Aeson.Value -> FileDraft
|
||||||
|
mkTmplFdWithData src tmplData = mkTmplFdWithDstAndData src (SP.castRel src) (Just tmplData)
|
||||||
|
|
||||||
|
mkTmplFdWithDstAndData ::
|
||||||
Path' (Rel WebAppTemplatesDir) File' ->
|
Path' (Rel WebAppTemplatesDir) File' ->
|
||||||
Path' (Rel WebAppRootDir) File' ->
|
Path' (Rel WebAppRootDir) File' ->
|
||||||
Maybe Aeson.Value ->
|
Maybe Aeson.Value ->
|
||||||
FileDraft
|
FileDraft
|
||||||
makeTemplateFD srcPathInWebAppTemplatesDir dstPathInWebAppRootDir tmplData =
|
mkTmplFdWithDstAndData srcPathInWebAppTemplatesDir dstPathInWebAppRootDir tmplData =
|
||||||
createTemplateFileDraft
|
createTemplateFileDraft
|
||||||
(webAppRootDirInProjectRootDir </> dstPathInWebAppRootDir)
|
(webAppRootDirInProjectRootDir </> dstPathInWebAppRootDir)
|
||||||
(webAppTemplatesDirInTemplatesDir </> srcPathInWebAppTemplatesDir)
|
(webAppTemplatesDirInTemplatesDir </> srcPathInWebAppTemplatesDir)
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.WebAppGenerator.OperationsGenerator
|
module Wasp.Generator.WebAppGenerator.OperationsGenerator
|
||||||
( genOperations,
|
( genOperations,
|
||||||
)
|
)
|
||||||
@ -10,44 +12,44 @@ import Data.Aeson
|
|||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
import Data.Maybe (fromJust)
|
import Data.Maybe (fromJust)
|
||||||
import StrongPath (File', Path', Rel', parseRelFile, reldir, relfile, (</>))
|
import StrongPath (File', Path', Rel', parseRelFile, reldir, relfile, (</>))
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.Action as AS.Action
|
||||||
|
import qualified Wasp.AppSpec.Operation as AS.Operation
|
||||||
|
import qualified Wasp.AppSpec.Query as AS.Query
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
import qualified Wasp.Generator.ServerGenerator as ServerGenerator
|
||||||
import qualified Wasp.Generator.ServerGenerator.OperationsRoutesG as ServerOperationsRoutesG
|
import qualified Wasp.Generator.ServerGenerator.OperationsRoutesG as ServerOperationsRoutesG
|
||||||
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
||||||
import qualified Wasp.Generator.WebAppGenerator.OperationsGenerator.ResourcesG as Resources
|
import qualified Wasp.Generator.WebAppGenerator.OperationsGenerator.ResourcesG as Resources
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.Action as Wasp.Action
|
|
||||||
import qualified Wasp.Wasp.Operation as Wasp.Operation
|
|
||||||
import qualified Wasp.Wasp.Query as Wasp.Query
|
|
||||||
|
|
||||||
genOperations :: Wasp -> [FileDraft]
|
genOperations :: AppSpec -> [FileDraft]
|
||||||
genOperations wasp =
|
genOperations spec =
|
||||||
concat
|
concat
|
||||||
[ genQueries wasp,
|
[ genQueries spec,
|
||||||
genActions wasp,
|
genActions spec,
|
||||||
[C.makeSimpleTemplateFD (C.asTmplFile [relfile|src/operations/index.js|]) wasp],
|
[C.mkTmplFd $ C.asTmplFile [relfile|src/operations/index.js|]],
|
||||||
Resources.genResources wasp
|
Resources.genResources spec
|
||||||
]
|
]
|
||||||
|
|
||||||
genQueries :: Wasp -> [FileDraft]
|
genQueries :: AppSpec -> [FileDraft]
|
||||||
genQueries wasp =
|
genQueries spec =
|
||||||
map (genQuery wasp) (Wasp.getQueries wasp)
|
map (genQuery spec) (AS.getQueries spec)
|
||||||
++ [C.makeSimpleTemplateFD (C.asTmplFile [relfile|src/queries/index.js|]) wasp]
|
++ [C.mkTmplFd $ C.asTmplFile [relfile|src/queries/index.js|]]
|
||||||
|
|
||||||
genActions :: Wasp -> [FileDraft]
|
genActions :: AppSpec -> [FileDraft]
|
||||||
genActions wasp =
|
genActions spec =
|
||||||
map (genAction wasp) (Wasp.getActions wasp)
|
map (genAction spec) (AS.getActions spec)
|
||||||
|
|
||||||
genQuery :: Wasp -> Wasp.Query.Query -> FileDraft
|
genQuery :: AppSpec -> (String, AS.Query.Query) -> FileDraft
|
||||||
genQuery _ query = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genQuery _ (queryName, query) = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
tmplFile = C.asTmplFile [relfile|src/queries/_query.js|]
|
tmplFile = C.asTmplFile [relfile|src/queries/_query.js|]
|
||||||
|
|
||||||
dstFile = C.asWebAppFile $ [reldir|src/queries/|] </> fromJust (getOperationDstFileName operation)
|
dstFile = C.asWebAppFile $ [reldir|src/queries/|] </> fromJust (getOperationDstFileName operation)
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "queryFnName" .= Wasp.Query._name query,
|
[ "queryFnName" .= (queryName :: String),
|
||||||
"queryRoute"
|
"queryRoute"
|
||||||
.= ( ServerGenerator.operationsRouteInRootRouter
|
.= ( ServerGenerator.operationsRouteInRootRouter
|
||||||
++ "/"
|
++ "/"
|
||||||
@ -55,17 +57,17 @@ genQuery _ query = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
|||||||
),
|
),
|
||||||
"entitiesArray" .= makeJsArrayOfEntityNames operation
|
"entitiesArray" .= makeJsArrayOfEntityNames operation
|
||||||
]
|
]
|
||||||
operation = Wasp.Operation.QueryOp query
|
operation = AS.Operation.QueryOp queryName query
|
||||||
|
|
||||||
genAction :: Wasp -> Wasp.Action.Action -> FileDraft
|
genAction :: AppSpec -> (String, AS.Action.Action) -> FileDraft
|
||||||
genAction _ action = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
genAction _ (actionName, action) = C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)
|
||||||
where
|
where
|
||||||
tmplFile = C.asTmplFile [relfile|src/actions/_action.js|]
|
tmplFile = C.asTmplFile [relfile|src/actions/_action.js|]
|
||||||
|
|
||||||
dstFile = C.asWebAppFile $ [reldir|src/actions/|] </> fromJust (getOperationDstFileName operation)
|
dstFile = C.asWebAppFile $ [reldir|src/actions/|] </> fromJust (getOperationDstFileName operation)
|
||||||
tmplData =
|
tmplData =
|
||||||
object
|
object
|
||||||
[ "actionFnName" .= Wasp.Action._name action,
|
[ "actionFnName" .= (actionName :: String),
|
||||||
"actionRoute"
|
"actionRoute"
|
||||||
.= ( ServerGenerator.operationsRouteInRootRouter
|
.= ( ServerGenerator.operationsRouteInRootRouter
|
||||||
++ "/"
|
++ "/"
|
||||||
@ -73,14 +75,14 @@ genAction _ action = C.makeTemplateFD tmplFile dstFile (Just tmplData)
|
|||||||
),
|
),
|
||||||
"entitiesArray" .= makeJsArrayOfEntityNames operation
|
"entitiesArray" .= makeJsArrayOfEntityNames operation
|
||||||
]
|
]
|
||||||
operation = Wasp.Operation.ActionOp action
|
operation = AS.Operation.ActionOp actionName action
|
||||||
|
|
||||||
-- | Generates string that is JS array containing names (as strings) of entities being used by given operation.
|
-- | Generates string that is JS array containing names (as strings) of entities being used by given operation.
|
||||||
-- E.g. "['Task', 'Project']"
|
-- E.g. "['Task', 'Project']"
|
||||||
makeJsArrayOfEntityNames :: Wasp.Operation.Operation -> String
|
makeJsArrayOfEntityNames :: AS.Operation.Operation -> String
|
||||||
makeJsArrayOfEntityNames operation = "[" ++ intercalate ", " entityStrings ++ "]"
|
makeJsArrayOfEntityNames operation = "[" ++ intercalate ", " entityStrings ++ "]"
|
||||||
where
|
where
|
||||||
entityStrings = maybe [] (map (\x -> "'" ++ x ++ "'")) (Wasp.Operation.getEntities operation)
|
entityStrings = maybe [] (map $ \x -> "'" ++ AS.refName x ++ "'") (AS.Operation.getEntities operation)
|
||||||
|
|
||||||
getOperationDstFileName :: Wasp.Operation.Operation -> Maybe (Path' Rel' File')
|
getOperationDstFileName :: AS.Operation.Operation -> Maybe (Path' Rel' File')
|
||||||
getOperationDstFileName operation = parseRelFile (Wasp.Operation.getName operation ++ ".js")
|
getOperationDstFileName operation = parseRelFile (AS.Operation.getName operation ++ ".js")
|
||||||
|
@ -5,12 +5,12 @@ where
|
|||||||
|
|
||||||
import Data.Aeson (object)
|
import Data.Aeson (object)
|
||||||
import StrongPath (relfile)
|
import StrongPath (relfile)
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
|
|
||||||
genResources :: Wasp -> [FileDraft]
|
genResources :: AppSpec -> [FileDraft]
|
||||||
genResources _ = [C.makeTemplateFD tmplFile dstFile (Just tmplData)]
|
genResources _ = [C.mkTmplFdWithDstAndData tmplFile dstFile (Just tmplData)]
|
||||||
where
|
where
|
||||||
tmplFile = C.asTmplFile [relfile|src/operations/resources.js|]
|
tmplFile = C.asTmplFile [relfile|src/operations/resources.js|]
|
||||||
dstFile = C.asWebAppFile [relfile|src/operations/resources.js|] -- TODO: Un-hardcode this by combining path to operations dir with path to resources file in it.
|
dstFile = C.asWebAppFile [relfile|src/operations/resources.js|] -- TODO: Un-hardcode this by combining path to operations dir with path to resources file in it.
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
{-# LANGUAGE TypeApplications #-}
|
||||||
|
|
||||||
module Wasp.Generator.WebAppGenerator.RouterGenerator
|
module Wasp.Generator.WebAppGenerator.RouterGenerator
|
||||||
( generateRouter,
|
( generateRouter,
|
||||||
)
|
)
|
||||||
@ -5,17 +7,18 @@ where
|
|||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
import Data.Aeson (ToJSON (..), object, (.=))
|
||||||
import Data.List (find)
|
import Data.List (find)
|
||||||
import Data.Maybe (fromJust, fromMaybe, isJust)
|
import Data.Maybe (fromMaybe)
|
||||||
import StrongPath (reldir, relfile, (</>))
|
import StrongPath (reldir, relfile, (</>))
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
|
import qualified System.FilePath as FP
|
||||||
|
import Wasp.AppSpec (AppSpec)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
|
import qualified Wasp.AppSpec.ExtImport as AS.ExtImport
|
||||||
|
import qualified Wasp.AppSpec.Page as AS.Page
|
||||||
|
import qualified Wasp.AppSpec.Route as AS.Route
|
||||||
import Wasp.Generator.FileDraft (FileDraft)
|
import Wasp.Generator.FileDraft (FileDraft)
|
||||||
import Wasp.Generator.WebAppGenerator.Common (asTmplFile, asWebAppSrcFile)
|
import Wasp.Generator.WebAppGenerator.Common (asTmplFile, asWebAppSrcFile)
|
||||||
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
import qualified Wasp.Generator.WebAppGenerator.Common as C
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
import qualified Wasp.Wasp.JsImport as Wasp.JsImport
|
|
||||||
import qualified Wasp.Wasp.Page as Wasp.Page
|
|
||||||
import qualified Wasp.Wasp.Route as Wasp.Route
|
|
||||||
|
|
||||||
data RouterTemplateData = RouterTemplateData
|
data RouterTemplateData = RouterTemplateData
|
||||||
{ _routes :: ![RouteTemplateData],
|
{ _routes :: ![RouteTemplateData],
|
||||||
@ -56,47 +59,52 @@ instance ToJSON PageTemplateData where
|
|||||||
"importFrom" .= _importFrom pageTD
|
"importFrom" .= _importFrom pageTD
|
||||||
]
|
]
|
||||||
|
|
||||||
generateRouter :: Wasp -> FileDraft
|
generateRouter :: AppSpec -> FileDraft
|
||||||
generateRouter wasp =
|
generateRouter spec =
|
||||||
C.makeTemplateFD
|
C.mkTmplFdWithDstAndData
|
||||||
(asTmplFile $ [reldir|src|] </> routerPath)
|
(asTmplFile $ [reldir|src|] </> routerPath)
|
||||||
targetPath
|
targetPath
|
||||||
(Just $ toJSON templateData)
|
(Just $ toJSON templateData)
|
||||||
where
|
where
|
||||||
routerPath = [relfile|router.js|]
|
routerPath = [relfile|router.js|]
|
||||||
templateData = createRouterTemplateData wasp
|
templateData = createRouterTemplateData spec
|
||||||
targetPath = C.webAppSrcDirInWebAppRootDir </> asWebAppSrcFile routerPath
|
targetPath = C.webAppSrcDirInWebAppRootDir </> asWebAppSrcFile routerPath
|
||||||
|
|
||||||
createRouterTemplateData :: Wasp -> RouterTemplateData
|
createRouterTemplateData :: AppSpec -> RouterTemplateData
|
||||||
createRouterTemplateData wasp =
|
createRouterTemplateData spec =
|
||||||
RouterTemplateData
|
RouterTemplateData
|
||||||
{ _routes = routes,
|
{ _routes = routes,
|
||||||
_pagesToImport = pages,
|
_pagesToImport = pages,
|
||||||
_isAuthEnabled = isJust $ Wasp.getAuth wasp
|
_isAuthEnabled = AS.isAuthEnabled spec
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
routes = map (createRouteTemplateData wasp) $ Wasp.getRoutes wasp
|
routes = map (createRouteTemplateData spec) $ AS.getRoutes spec
|
||||||
pages = map createPageTemplateData $ Wasp.getPages wasp
|
pages = map createPageTemplateData $ AS.getPages spec
|
||||||
|
|
||||||
createRouteTemplateData :: Wasp -> Wasp.Route.Route -> RouteTemplateData
|
createRouteTemplateData :: AppSpec -> (String, AS.Route.Route) -> RouteTemplateData
|
||||||
createRouteTemplateData wasp route =
|
createRouteTemplateData spec namedRoute@(_, route) =
|
||||||
RouteTemplateData
|
RouteTemplateData
|
||||||
{ _urlPath = Wasp.Route._urlPath route,
|
{ _urlPath = AS.Route.path route,
|
||||||
_targetComponent = determineRouteTargetComponent wasp route
|
_targetComponent = determineRouteTargetComponent spec namedRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
determineRouteTargetComponent :: Wasp -> Wasp.Route.Route -> String
|
determineRouteTargetComponent :: AppSpec -> (String, AS.Route.Route) -> String
|
||||||
determineRouteTargetComponent wasp route =
|
determineRouteTargetComponent spec (_, route) =
|
||||||
maybe
|
maybe
|
||||||
targetPageName
|
targetPageName
|
||||||
determineRouteTargetComponent'
|
determineRouteTargetComponent'
|
||||||
(Wasp.Page._authRequired targetPage)
|
(AS.Page.authRequired $ snd targetPage)
|
||||||
where
|
where
|
||||||
targetPageName = Wasp.Route._targetPage route
|
targetPageName = AS.refName (AS.Route.to route :: AS.Ref AS.Page.Page)
|
||||||
targetPage =
|
targetPage =
|
||||||
fromMaybe
|
fromMaybe
|
||||||
(error $ "Can't find page with name '" ++ targetPageName ++ "', pointed to by route '" ++ Wasp.Route._urlPath route ++ "'")
|
( error $
|
||||||
(find ((==) targetPageName . Wasp.Page._name) (Wasp.getPages wasp))
|
"Can't find page with name '" ++ targetPageName
|
||||||
|
++ "', pointed to by route '"
|
||||||
|
++ AS.Route.path route
|
||||||
|
++ "'"
|
||||||
|
)
|
||||||
|
(find ((==) targetPageName . fst) (AS.getPages spec))
|
||||||
|
|
||||||
determineRouteTargetComponent' :: Bool -> String
|
determineRouteTargetComponent' :: Bool -> String
|
||||||
determineRouteTargetComponent' authRequired =
|
determineRouteTargetComponent' authRequired =
|
||||||
@ -105,21 +113,20 @@ determineRouteTargetComponent wasp route =
|
|||||||
"createAuthRequiredPage(" ++ targetPageName ++ ")"
|
"createAuthRequiredPage(" ++ targetPageName ++ ")"
|
||||||
else targetPageName
|
else targetPageName
|
||||||
|
|
||||||
createPageTemplateData :: Wasp.Page.Page -> PageTemplateData
|
createPageTemplateData :: (String, AS.Page.Page) -> PageTemplateData
|
||||||
createPageTemplateData page =
|
createPageTemplateData page =
|
||||||
PageTemplateData
|
PageTemplateData
|
||||||
{ _importFrom =
|
{ _importFrom = relPathToExtSrcDir FP.</> SP.fromRelFileP (AS.ExtImport.path pageComponent),
|
||||||
relPathToExtSrcDir
|
_importWhat = case AS.ExtImport.name pageComponent of
|
||||||
++ SP.fromRelFileP (fromJust $ SP.relFileToPosix $ Wasp.JsImport._from pageComponent),
|
AS.ExtImport.ExtImportModule _ -> pageName
|
||||||
_importWhat = case Wasp.JsImport._namedImports pageComponent of
|
AS.ExtImport.ExtImportField identifier -> "{ " ++ identifier ++ " as " ++ pageName ++ " }"
|
||||||
-- If no named imports, we go with the default import.
|
|
||||||
[] -> pageName
|
|
||||||
[namedImport] -> "{ " ++ namedImport ++ " as " ++ pageName ++ " }"
|
|
||||||
_ -> error "Only one named import can be provided for a page."
|
|
||||||
}
|
}
|
||||||
where
|
where
|
||||||
relPathToExtSrcDir :: FilePath
|
relPathToExtSrcDir :: FilePath
|
||||||
relPathToExtSrcDir = "./ext-src/"
|
relPathToExtSrcDir = "./ext-src/"
|
||||||
|
|
||||||
pageName = Wasp.Page._name page
|
pageName :: String
|
||||||
pageComponent = Wasp.Page._component page
|
pageName = fst page
|
||||||
|
|
||||||
|
pageComponent :: AS.ExtImport.ExtImport
|
||||||
|
pageComponent = AS.Page.component $ snd page
|
||||||
|
@ -11,16 +11,17 @@ import Data.List (find, isSuffixOf)
|
|||||||
import StrongPath (Abs, Dir, File', Path', relfile)
|
import StrongPath (Abs, Dir, File', Path', relfile)
|
||||||
import qualified StrongPath as SP
|
import qualified StrongPath as SP
|
||||||
import System.Directory (doesDirectoryExist, doesFileExist)
|
import System.Directory (doesDirectoryExist, doesFileExist)
|
||||||
|
import qualified Wasp.Analyzer as Analyzer
|
||||||
|
import Wasp.Analyzer.AnalyzeError (getErrorMessageAndCtx)
|
||||||
|
import qualified Wasp.AppSpec as AS
|
||||||
import Wasp.Common (DbMigrationsDir, WaspProjectDir, dbMigrationsDirInWaspProjectDir)
|
import Wasp.Common (DbMigrationsDir, WaspProjectDir, dbMigrationsDirInWaspProjectDir)
|
||||||
import Wasp.CompileOptions (CompileOptions)
|
import Wasp.CompileOptions (CompileOptions)
|
||||||
import qualified Wasp.CompileOptions as CompileOptions
|
import qualified Wasp.CompileOptions as CompileOptions
|
||||||
|
import Wasp.Error (showCompilerErrorForTerminal)
|
||||||
import qualified Wasp.ExternalCode as ExternalCode
|
import qualified Wasp.ExternalCode as ExternalCode
|
||||||
import qualified Wasp.Generator as Generator
|
import qualified Wasp.Generator as Generator
|
||||||
import Wasp.Generator.Common (ProjectRootDir)
|
import Wasp.Generator.Common (ProjectRootDir)
|
||||||
import qualified Wasp.Parser as Parser
|
|
||||||
import qualified Wasp.Util.IO as Util.IO
|
import qualified Wasp.Util.IO as Util.IO
|
||||||
import Wasp.Wasp (Wasp)
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
|
|
||||||
type CompileError = String
|
type CompileError = String
|
||||||
|
|
||||||
@ -30,34 +31,33 @@ compile ::
|
|||||||
CompileOptions ->
|
CompileOptions ->
|
||||||
IO (Either CompileError ())
|
IO (Either CompileError ())
|
||||||
compile waspDir outDir options = do
|
compile waspDir outDir options = do
|
||||||
maybeWaspFile <- findWaspFile waspDir
|
maybeWaspFilePath <- findWaspFile waspDir
|
||||||
case maybeWaspFile of
|
case maybeWaspFilePath of
|
||||||
Nothing -> return $ Left "Couldn't find a single *.wasp file."
|
Nothing -> return $ Left "Couldn't find a single *.wasp file."
|
||||||
Just waspFile -> do
|
Just waspFilePath -> do
|
||||||
waspStr <- readFile (SP.toFilePath waspFile)
|
waspFileContent <- readFile (SP.fromAbsFile waspFilePath)
|
||||||
|
case Analyzer.analyze waspFileContent of
|
||||||
case Parser.parseWasp waspStr of
|
Left analyzeError ->
|
||||||
Left err -> return $ Left (show err)
|
return $
|
||||||
Right wasp -> do
|
Left $
|
||||||
|
showCompilerErrorForTerminal
|
||||||
|
(waspFilePath, waspFileContent)
|
||||||
|
(getErrorMessageAndCtx analyzeError)
|
||||||
|
Right decls -> do
|
||||||
|
externalCodeFiles <-
|
||||||
|
ExternalCode.readFiles (CompileOptions.externalCodeDirPath options)
|
||||||
maybeDotEnvFile <- findDotEnvFile waspDir
|
maybeDotEnvFile <- findDotEnvFile waspDir
|
||||||
maybeMigrationsDir <- findMigrationsDir waspDir
|
maybeMigrationsDir <- findMigrationsDir waspDir
|
||||||
( wasp
|
let appSpec =
|
||||||
`Wasp.setDotEnvFile` maybeDotEnvFile
|
AS.AppSpec
|
||||||
`Wasp.setMigrationsDir` maybeMigrationsDir
|
{ AS.decls = decls,
|
||||||
`enrichWaspASTBasedOnCompileOptions` options
|
AS.externalCodeFiles = externalCodeFiles,
|
||||||
)
|
AS.externalCodeDirPath = CompileOptions.externalCodeDirPath options,
|
||||||
>>= generateCode
|
AS.migrationsDir = maybeMigrationsDir,
|
||||||
where
|
AS.dotEnvFile = maybeDotEnvFile,
|
||||||
generateCode wasp = Generator.writeWebAppCode wasp outDir options >> return (Right ())
|
AS.isBuild = CompileOptions.isBuild options
|
||||||
|
}
|
||||||
enrichWaspASTBasedOnCompileOptions :: Wasp -> CompileOptions -> IO Wasp
|
Right <$> Generator.writeWebAppCode appSpec outDir
|
||||||
enrichWaspASTBasedOnCompileOptions wasp options = do
|
|
||||||
externalCodeFiles <- ExternalCode.readFiles (CompileOptions.externalCodeDirPath options)
|
|
||||||
return
|
|
||||||
( wasp
|
|
||||||
`Wasp.setExternalCodeFiles` externalCodeFiles
|
|
||||||
`Wasp.setIsBuild` CompileOptions.isBuild options
|
|
||||||
)
|
|
||||||
|
|
||||||
findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File'))
|
findWaspFile :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs File'))
|
||||||
findWaspFile waspDir = do
|
findWaspFile waspDir = do
|
||||||
@ -74,7 +74,9 @@ findDotEnvFile waspDir = do
|
|||||||
dotEnvExists <- doesFileExist (SP.toFilePath dotEnvAbsPath)
|
dotEnvExists <- doesFileExist (SP.toFilePath dotEnvAbsPath)
|
||||||
return $ if dotEnvExists then Just dotEnvAbsPath else Nothing
|
return $ if dotEnvExists then Just dotEnvAbsPath else Nothing
|
||||||
|
|
||||||
findMigrationsDir :: Path' Abs (Dir WaspProjectDir) -> IO (Maybe (Path' Abs (Dir DbMigrationsDir)))
|
findMigrationsDir ::
|
||||||
|
Path' Abs (Dir WaspProjectDir) ->
|
||||||
|
IO (Maybe (Path' Abs (Dir DbMigrationsDir)))
|
||||||
findMigrationsDir waspDir = do
|
findMigrationsDir waspDir = do
|
||||||
let migrationsAbsPath = waspDir SP.</> dbMigrationsDirInWaspProjectDir
|
let migrationsAbsPath = waspDir SP.</> dbMigrationsDirInWaspProjectDir
|
||||||
migrationsExists <- doesDirectoryExist $ SP.fromAbsDir migrationsAbsPath
|
migrationsExists <- doesDirectoryExist $ SP.fromAbsDir migrationsAbsPath
|
||||||
|
@ -1,88 +0,0 @@
|
|||||||
module Wasp.Parser
|
|
||||||
( parseWasp,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Text.Parsec (ParseError, eof, many, many1, (<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import Wasp.Lexer
|
|
||||||
import qualified Wasp.Parser.Action as Parser.Action
|
|
||||||
import Wasp.Parser.App (app)
|
|
||||||
import Wasp.Parser.Auth (auth)
|
|
||||||
import Wasp.Parser.Common (runWaspParser)
|
|
||||||
import Wasp.Parser.Db (db)
|
|
||||||
import Wasp.Parser.Entity (entity)
|
|
||||||
import Wasp.Parser.JsImport (jsImport)
|
|
||||||
import qualified Wasp.Parser.NpmDependencies as Parser.NpmDependencies
|
|
||||||
import Wasp.Parser.Page (page)
|
|
||||||
import qualified Wasp.Parser.Query as Parser.Query
|
|
||||||
import Wasp.Parser.Route (route)
|
|
||||||
import qualified Wasp.Parser.Server as Parser.Server
|
|
||||||
import qualified Wasp.Wasp as Wasp
|
|
||||||
|
|
||||||
waspElement :: Parser Wasp.WaspElement
|
|
||||||
waspElement =
|
|
||||||
waspElementApp
|
|
||||||
<|> waspElementAuth
|
|
||||||
<|> waspElementPage
|
|
||||||
<|> waspElementDb
|
|
||||||
<|> waspElementRoute
|
|
||||||
<|> waspElementEntity
|
|
||||||
<|> waspElementQuery
|
|
||||||
<|> waspElementAction
|
|
||||||
<|> waspElementNpmDependencies
|
|
||||||
<|> waspElementServer
|
|
||||||
|
|
||||||
waspElementApp :: Parser Wasp.WaspElement
|
|
||||||
waspElementApp = Wasp.WaspElementApp <$> app
|
|
||||||
|
|
||||||
waspElementAuth :: Parser Wasp.WaspElement
|
|
||||||
waspElementAuth = Wasp.WaspElementAuth <$> auth
|
|
||||||
|
|
||||||
waspElementDb :: Parser Wasp.WaspElement
|
|
||||||
waspElementDb = Wasp.WaspElementDb <$> db
|
|
||||||
|
|
||||||
waspElementPage :: Parser Wasp.WaspElement
|
|
||||||
waspElementPage = Wasp.WaspElementPage <$> page
|
|
||||||
|
|
||||||
waspElementRoute :: Parser Wasp.WaspElement
|
|
||||||
waspElementRoute = Wasp.WaspElementRoute <$> route
|
|
||||||
|
|
||||||
waspElementEntity :: Parser Wasp.WaspElement
|
|
||||||
waspElementEntity = Wasp.WaspElementEntity <$> entity
|
|
||||||
|
|
||||||
waspElementQuery :: Parser Wasp.WaspElement
|
|
||||||
waspElementQuery = Wasp.WaspElementQuery <$> Parser.Query.query
|
|
||||||
|
|
||||||
waspElementAction :: Parser Wasp.WaspElement
|
|
||||||
waspElementAction = Wasp.WaspElementAction <$> Parser.Action.action
|
|
||||||
|
|
||||||
waspElementServer :: Parser Wasp.WaspElement
|
|
||||||
waspElementServer = Wasp.WaspElementServer <$> Parser.Server.server
|
|
||||||
|
|
||||||
waspElementNpmDependencies :: Parser Wasp.WaspElement
|
|
||||||
waspElementNpmDependencies = Wasp.WaspElementNpmDependencies <$> Parser.NpmDependencies.npmDependencies
|
|
||||||
|
|
||||||
-- | Top level parser, produces Wasp.
|
|
||||||
waspParser :: Parser Wasp.Wasp
|
|
||||||
waspParser = do
|
|
||||||
-- NOTE(matija): this is the only place we need to use whiteSpace, to skip empty lines
|
|
||||||
-- and comments in the beginning of file. All other used parsers are lexeme parsers
|
|
||||||
-- so they do it themselves.
|
|
||||||
whiteSpace
|
|
||||||
|
|
||||||
jsImports <- many jsImport
|
|
||||||
|
|
||||||
waspElems <- many1 waspElement
|
|
||||||
|
|
||||||
eof
|
|
||||||
|
|
||||||
-- TODO(matija): after we parsed everything, we should do semantic analysis
|
|
||||||
-- e.g. check there is only 1 title - if not, throw a meaningful error.
|
|
||||||
-- Also, check there is at least one Page defined.
|
|
||||||
|
|
||||||
return $ Wasp.fromWaspElems waspElems `Wasp.setJsImports` jsImports
|
|
||||||
|
|
||||||
-- | Top level parser executor.
|
|
||||||
parseWasp :: String -> Either ParseError Wasp.Wasp
|
|
||||||
parseWasp = runWaspParser waspParser
|
|
@ -1,24 +0,0 @@
|
|||||||
module Wasp.Parser.Action
|
|
||||||
( action,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as C
|
|
||||||
import qualified Wasp.Parser.Operation as Operation
|
|
||||||
import Wasp.Wasp.Action (Action)
|
|
||||||
import qualified Wasp.Wasp.Action as Action
|
|
||||||
|
|
||||||
action :: Parser Action
|
|
||||||
action = do
|
|
||||||
(name, props) <- C.waspElementNameAndClosureContent L.reservedNameAction Operation.properties
|
|
||||||
return
|
|
||||||
Action.Action
|
|
||||||
{ Action._name = name,
|
|
||||||
Action._jsFunction =
|
|
||||||
fromMaybe (error "Action js function is missing.") (Operation.getJsFunctionFromProps props),
|
|
||||||
Action._entities = Operation.getEntitiesFromProps props,
|
|
||||||
Action._auth = Operation.getAuthEnabledFromProps props
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
module Wasp.Parser.App
|
|
||||||
( app,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (listToMaybe)
|
|
||||||
import Text.Parsec
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import Wasp.Lexer
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import Wasp.Parser.Common
|
|
||||||
import qualified Wasp.Wasp.App as App
|
|
||||||
|
|
||||||
-- | A type that describes supported app properties.
|
|
||||||
data AppProperty
|
|
||||||
= Title !String
|
|
||||||
| Favicon !String
|
|
||||||
| Head [String]
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
-- | Parses supported app properties, expects format "key1: value1, key2: value2, ..."
|
|
||||||
appProperties :: Parser [AppProperty]
|
|
||||||
appProperties =
|
|
||||||
commaSep1 $
|
|
||||||
appPropertyTitle
|
|
||||||
<|> appPropertyFavicon
|
|
||||||
<|> appPropertyHead
|
|
||||||
|
|
||||||
appPropertyTitle :: Parser AppProperty
|
|
||||||
appPropertyTitle = Title <$> waspPropertyStringLiteral "title"
|
|
||||||
|
|
||||||
appPropertyFavicon :: Parser AppProperty
|
|
||||||
-- TODO(matija): 'fav.png' currently does not work because of '.'. Support it.
|
|
||||||
appPropertyFavicon = Favicon <$> waspPropertyStringLiteral "favicon"
|
|
||||||
|
|
||||||
appPropertyHead :: Parser AppProperty
|
|
||||||
appPropertyHead = Head <$> waspProperty "head" (L.brackets $ L.commaSep1 L.stringLiteral)
|
|
||||||
|
|
||||||
-- TODO(matija): unsafe, what if empty list?
|
|
||||||
getAppTitle :: [AppProperty] -> String
|
|
||||||
getAppTitle ps = head $ [t | Title t <- ps]
|
|
||||||
|
|
||||||
getAppHead :: [AppProperty] -> Maybe [String]
|
|
||||||
getAppHead ps = listToMaybe [hs | Head hs <- ps]
|
|
||||||
|
|
||||||
-- | Top level parser, parses App.
|
|
||||||
app :: Parser App.App
|
|
||||||
app = do
|
|
||||||
(appName, appProps) <- waspElementNameAndClosureContent reservedNameApp appProperties
|
|
||||||
|
|
||||||
return
|
|
||||||
App.App
|
|
||||||
{ App.appName = appName,
|
|
||||||
App.appTitle = getAppTitle appProps,
|
|
||||||
App.appHead = getAppHead appProps
|
|
||||||
-- TODO(matija): add favicon.
|
|
||||||
}
|
|
@ -1,85 +0,0 @@
|
|||||||
module Wasp.Parser.Auth
|
|
||||||
( auth,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Control.Monad (when)
|
|
||||||
import Text.Parsec (try, (<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as P
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
|
|
||||||
auth :: Parser Wasp.Auth.Auth
|
|
||||||
auth = do
|
|
||||||
L.reserved L.reservedNameAuth
|
|
||||||
authProperties <- P.waspClosure (L.commaSep1 authProperty)
|
|
||||||
|
|
||||||
let userEntityProps = [s | AuthPropertyUserEntity s <- authProperties]
|
|
||||||
failIfPropMissing propUserEntityName userEntityProps
|
|
||||||
|
|
||||||
let methodsProps = [ms | AuthPropertyMethods ms <- authProperties]
|
|
||||||
failIfPropMissing propMethodsName methodsProps
|
|
||||||
|
|
||||||
let failRedirectProps = [r | AuthPropertyOnAuthFailedRedirectTo r <- authProperties]
|
|
||||||
failIfPropMissing propOnAuthFailedRedirectToName failRedirectProps
|
|
||||||
|
|
||||||
let successRedirectProps = [r | AuthPropertyOnAuthSucceededRedirectTo r <- authProperties]
|
|
||||||
|
|
||||||
return
|
|
||||||
Wasp.Auth.Auth
|
|
||||||
{ Wasp.Auth._userEntity = head userEntityProps,
|
|
||||||
Wasp.Auth._methods = head methodsProps,
|
|
||||||
Wasp.Auth._onAuthFailedRedirectTo = head failRedirectProps,
|
|
||||||
Wasp.Auth._onAuthSucceededRedirectTo = headWithDefault "/" successRedirectProps
|
|
||||||
}
|
|
||||||
|
|
||||||
headWithDefault :: a -> [a] -> a
|
|
||||||
headWithDefault d ps = if null ps then d else head ps
|
|
||||||
|
|
||||||
-- TODO(matija): this should be extracted if we want to use in other places too.
|
|
||||||
failIfPropMissing :: (Applicative m, MonadFail m) => String -> [p] -> m ()
|
|
||||||
failIfPropMissing propName ps = when (null ps) $ fail errorMsg
|
|
||||||
where
|
|
||||||
errorMsg = propName ++ " is required!"
|
|
||||||
|
|
||||||
-- Auxiliary data structure used by parser.
|
|
||||||
data AuthProperty
|
|
||||||
= AuthPropertyUserEntity String
|
|
||||||
| AuthPropertyMethods [Wasp.Auth.AuthMethod]
|
|
||||||
| AuthPropertyOnAuthFailedRedirectTo String
|
|
||||||
| AuthPropertyOnAuthSucceededRedirectTo String
|
|
||||||
|
|
||||||
propUserEntityName :: String
|
|
||||||
propUserEntityName = "userEntity"
|
|
||||||
|
|
||||||
propMethodsName :: String
|
|
||||||
propMethodsName = "methods"
|
|
||||||
|
|
||||||
propOnAuthFailedRedirectToName :: String
|
|
||||||
propOnAuthFailedRedirectToName = "onAuthFailedRedirectTo"
|
|
||||||
|
|
||||||
-- Sub-parsers
|
|
||||||
|
|
||||||
authProperty :: Parser AuthProperty
|
|
||||||
authProperty =
|
|
||||||
authPropertyUserEntity
|
|
||||||
<|> authPropertyMethods
|
|
||||||
<|> (try authPropertyOnAuthFailedRedirectTo <|> authPropertyOnAuthSucceededRedirectTo)
|
|
||||||
|
|
||||||
authPropertyOnAuthFailedRedirectTo :: Parser AuthProperty
|
|
||||||
authPropertyOnAuthFailedRedirectTo =
|
|
||||||
AuthPropertyOnAuthFailedRedirectTo <$> P.waspPropertyStringLiteral "onAuthFailedRedirectTo"
|
|
||||||
|
|
||||||
authPropertyOnAuthSucceededRedirectTo :: Parser AuthProperty
|
|
||||||
authPropertyOnAuthSucceededRedirectTo =
|
|
||||||
AuthPropertyOnAuthSucceededRedirectTo <$> P.waspPropertyStringLiteral "onAuthSucceededRedirectTo"
|
|
||||||
|
|
||||||
authPropertyUserEntity :: Parser AuthProperty
|
|
||||||
authPropertyUserEntity = AuthPropertyUserEntity <$> P.waspProperty "userEntity" L.identifier
|
|
||||||
|
|
||||||
authPropertyMethods :: Parser AuthProperty
|
|
||||||
authPropertyMethods = AuthPropertyMethods <$> P.waspProperty "methods" (L.brackets $ L.commaSep1 authMethod)
|
|
||||||
|
|
||||||
authMethod :: Parser Wasp.Auth.AuthMethod
|
|
||||||
authMethod = L.symbol "EmailAndPassword" *> pure Wasp.Auth.EmailAndPassword
|
|
@ -1,180 +0,0 @@
|
|||||||
{-
|
|
||||||
Common functions used among Wasp parsers.
|
|
||||||
-}
|
|
||||||
|
|
||||||
module Wasp.Parser.Common where
|
|
||||||
|
|
||||||
import qualified Data.Text as T
|
|
||||||
import StrongPath (File, Path, Posix, Rel, System)
|
|
||||||
import qualified StrongPath as SP
|
|
||||||
import Text.Parsec
|
|
||||||
( ParseError,
|
|
||||||
anyChar,
|
|
||||||
manyTill,
|
|
||||||
parse,
|
|
||||||
try,
|
|
||||||
unexpected,
|
|
||||||
)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
|
|
||||||
-- | Runs given wasp parser on a specified input.
|
|
||||||
runWaspParser :: Parser a -> String -> Either ParseError a
|
|
||||||
runWaspParser waspParser input = parse waspParser sourceName input
|
|
||||||
where
|
|
||||||
-- NOTE(matija): this is used by Parsec only when reporting errors, but we currently
|
|
||||||
-- don't provide source name (e.g. .wasp file name) to this method so leaving it empty
|
|
||||||
-- for now.
|
|
||||||
sourceName = ""
|
|
||||||
|
|
||||||
-- TODO(matija): rename to just "waspElement"?
|
|
||||||
|
|
||||||
-- | Parses declaration of a wasp element (e.g. App or Page) and the closure content.
|
|
||||||
waspElementNameAndClosureContent ::
|
|
||||||
-- | Type of the wasp element (e.g. "app" or "page").
|
|
||||||
String ->
|
|
||||||
-- | Parser to be used for parsing closure content of the wasp element.
|
|
||||||
Parser a ->
|
|
||||||
-- | Name of the element and parsed closure content.
|
|
||||||
Parser (String, a)
|
|
||||||
waspElementNameAndClosureContent elementType closureContent =
|
|
||||||
waspElementNameAndClosure elementType (waspClosure closureContent)
|
|
||||||
|
|
||||||
-- | Parses declaration of a wasp element (e.g. App or Page) and the belonging closure.
|
|
||||||
waspElementNameAndClosure ::
|
|
||||||
-- | Element type
|
|
||||||
String ->
|
|
||||||
-- | Closure parser (needs to parse braces as well, not just the content)
|
|
||||||
Parser a ->
|
|
||||||
-- | Name of the element and parsed closure content.
|
|
||||||
Parser (String, a)
|
|
||||||
waspElementNameAndClosure elementType closure =
|
|
||||||
-- NOTE(matija): It is important to have `try` here because we don't want to consume the
|
|
||||||
-- content intended for other parsers.
|
|
||||||
-- E.g. if we tried to parse "entity-form" this parser would have been tried first for
|
|
||||||
-- "entity" and would consume "entity", so entity-form parser would also fail.
|
|
||||||
-- This way when entity parser fails, it will backtrack and allow
|
|
||||||
-- entity-form parser to succeed.
|
|
||||||
--
|
|
||||||
-- TODO(matija): should I push this try higher, to the specific case of entity parser
|
|
||||||
-- which is causing the trouble?
|
|
||||||
-- This way try will be executed in more cases where it is not neccessary, this
|
|
||||||
-- might not be the best for the performance and the clarity of error messages.
|
|
||||||
-- On the other hand, it is safer?
|
|
||||||
try $ do
|
|
||||||
L.reserved elementType
|
|
||||||
elementName <- L.identifier
|
|
||||||
closureContent <- closure
|
|
||||||
|
|
||||||
return (elementName, closureContent)
|
|
||||||
|
|
||||||
-- | Parses declaration of a wasp element linked to an entity.
|
|
||||||
-- E.g. "entity-form<Task> ..." or "action<Task> ..."
|
|
||||||
waspElementLinkedToEntity ::
|
|
||||||
-- | Type of the linked wasp element (e.g. "entity-form").
|
|
||||||
String ->
|
|
||||||
-- | Parser to be used for parsing body of the wasp element.
|
|
||||||
Parser a ->
|
|
||||||
-- | Name of the linked entity, element name and body.
|
|
||||||
Parser (String, String, a)
|
|
||||||
waspElementLinkedToEntity elementType bodyParser = do
|
|
||||||
L.reserved elementType
|
|
||||||
linkedEntityName <- L.angles L.identifier
|
|
||||||
elementName <- L.identifier
|
|
||||||
body <- bodyParser
|
|
||||||
return (linkedEntityName, elementName, body)
|
|
||||||
|
|
||||||
-- | Parses wasp property along with the key, "key: value".
|
|
||||||
waspProperty :: String -> Parser a -> Parser a
|
|
||||||
waspProperty key value = L.symbol key <* L.colon *> value
|
|
||||||
|
|
||||||
-- | Parses wasp property which has a string literal for a value.
|
|
||||||
-- e.g.: title: "my first app"
|
|
||||||
waspPropertyStringLiteral :: String -> Parser String
|
|
||||||
waspPropertyStringLiteral key = waspProperty key L.stringLiteral
|
|
||||||
|
|
||||||
-- | Parses wasp property which has a bool for a value. E.g.: "onEnter: true".
|
|
||||||
waspPropertyBool :: String -> Parser Bool
|
|
||||||
waspPropertyBool key = waspProperty key L.bool
|
|
||||||
|
|
||||||
-- | Parses wasp property which has an identifier as a key (E.g. field name or filter name).
|
|
||||||
-- E.g. within an entity-form {} we can set properties for a specific field with a closure of
|
|
||||||
-- form "FIELD_NAME: {...}" -> FIELD_NAME is then an identifier we need.
|
|
||||||
waspPropertyWithIdentifierAsKey :: Parser a -> Parser (String, a)
|
|
||||||
waspPropertyWithIdentifierAsKey valueP = do
|
|
||||||
identifier <- L.identifier <* L.colon
|
|
||||||
value <- valueP
|
|
||||||
|
|
||||||
return (identifier, value)
|
|
||||||
|
|
||||||
-- | Parses wasp closure, which is {...}. Returns parsed content within the closure.
|
|
||||||
waspClosure :: Parser a -> Parser a
|
|
||||||
waspClosure = L.braces
|
|
||||||
|
|
||||||
-- | Parses wasp property closure where property is an identifier whose value we also
|
|
||||||
-- need to retrieve.
|
|
||||||
-- E.g. within an entity-form {} we can set properties for a specific field with a closure of
|
|
||||||
-- form "FIELD_NAME: {...}" -> FIELD_NAME is then an identifier we need.
|
|
||||||
waspIdentifierClosure :: Parser a -> Parser (String, a)
|
|
||||||
waspIdentifierClosure = waspPropertyWithIdentifierAsKey . waspClosure
|
|
||||||
|
|
||||||
-- | Parses wasp property which has a closure for a value. Returns parsed content within the
|
|
||||||
-- closure.
|
|
||||||
waspPropertyClosure :: String -> Parser a -> Parser a
|
|
||||||
waspPropertyClosure key closureContent = waspProperty key (waspClosure closureContent)
|
|
||||||
|
|
||||||
-- | Parses wasp property which has a jsx closure for a value. Returns the content
|
|
||||||
-- within the closure.
|
|
||||||
waspPropertyJsxClosure :: String -> Parser String
|
|
||||||
waspPropertyJsxClosure key = waspProperty key waspJsxClosure
|
|
||||||
|
|
||||||
-- | Parses wasp property which has a css closure for a value. Returns the content
|
|
||||||
-- within the closure.
|
|
||||||
waspPropertyCssClosure :: String -> Parser String
|
|
||||||
waspPropertyCssClosure key = waspProperty key waspCssClosure
|
|
||||||
|
|
||||||
-- | Parses wasp jsx closure, which is {=jsx...jsx=}. Returns content within the closure.
|
|
||||||
waspJsxClosure :: Parser String
|
|
||||||
waspJsxClosure = waspNamedClosure "jsx"
|
|
||||||
|
|
||||||
-- | Parses wasp css closure, which is {=css...css=}. Returns content within the closure.
|
|
||||||
waspCssClosure :: Parser String
|
|
||||||
waspCssClosure = waspNamedClosure "css"
|
|
||||||
|
|
||||||
-- TODO(martin): write tests and comments.
|
|
||||||
|
|
||||||
-- | Parses named wasp closure, which is {=name...name=}. Returns content within the closure.
|
|
||||||
waspNamedClosure :: String -> Parser String
|
|
||||||
waspNamedClosure name = do
|
|
||||||
_ <- closureStart
|
|
||||||
strip <$> manyTill anyChar (try closureEnd)
|
|
||||||
where
|
|
||||||
closureStart = L.symbol ("{=" ++ name)
|
|
||||||
closureEnd = L.symbol (name ++ "=}")
|
|
||||||
|
|
||||||
-- | Parses a list of items that can be parsed with given parser.
|
|
||||||
-- For example, `waspList L.identifier` will parse "[foo, bar, t]" into ["foo", "bar", "t"].
|
|
||||||
waspList :: Parser a -> Parser [a]
|
|
||||||
waspList elementParser = L.brackets $ L.commaSep elementParser
|
|
||||||
|
|
||||||
-- | Removes leading and trailing spaces from a string.
|
|
||||||
strip :: String -> String
|
|
||||||
strip = T.unpack . T.strip . T.pack
|
|
||||||
|
|
||||||
-- | Parses relative file path, e.g. "my/file.txt".
|
|
||||||
relFilePathString :: Parser (Path System (Rel d) (File f))
|
|
||||||
relFilePathString = do
|
|
||||||
path <- L.stringLiteral
|
|
||||||
maybe
|
|
||||||
(unexpected $ "string \"" ++ path ++ "\": Expected relative file path.")
|
|
||||||
return
|
|
||||||
(SP.parseRelFile path)
|
|
||||||
|
|
||||||
-- | Parses relative posix file path, e.g. "my/file.txt".
|
|
||||||
relPosixFilePathString :: Parser (Path Posix (Rel d) (File f))
|
|
||||||
relPosixFilePathString = do
|
|
||||||
path <- L.stringLiteral
|
|
||||||
maybe
|
|
||||||
(unexpected $ "string \"" ++ path ++ "\": Expected relative file path.")
|
|
||||||
return
|
|
||||||
(SP.parseRelFileP path)
|
|
@ -1,41 +0,0 @@
|
|||||||
module Wasp.Parser.Db
|
|
||||||
( db,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (listToMaybe)
|
|
||||||
import Text.Parsec (try, (<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as P
|
|
||||||
import qualified Wasp.Wasp.Db as Wasp.Db
|
|
||||||
|
|
||||||
db :: Parser Wasp.Db.Db
|
|
||||||
db = do
|
|
||||||
L.reserved L.reservedNameDb
|
|
||||||
dbProperties <- P.waspClosure (L.commaSep1 dbProperty)
|
|
||||||
|
|
||||||
system <-
|
|
||||||
maybe
|
|
||||||
(fail "'system' property is required!")
|
|
||||||
return
|
|
||||||
(listToMaybe [p | DbPropertySystem p <- dbProperties])
|
|
||||||
|
|
||||||
return
|
|
||||||
Wasp.Db.Db
|
|
||||||
{ Wasp.Db._system = system
|
|
||||||
}
|
|
||||||
|
|
||||||
data DbProperty
|
|
||||||
= DbPropertySystem Wasp.Db.DbSystem
|
|
||||||
|
|
||||||
dbProperty :: Parser DbProperty
|
|
||||||
dbProperty =
|
|
||||||
dbPropertySystem
|
|
||||||
|
|
||||||
dbPropertySystem :: Parser DbProperty
|
|
||||||
dbPropertySystem = DbPropertySystem <$> P.waspProperty "system" dbPropertySystemValue
|
|
||||||
where
|
|
||||||
dbPropertySystemValue =
|
|
||||||
try (L.symbol "PostgreSQL" >> return Wasp.Db.PostgreSQL)
|
|
||||||
<|> try (L.symbol "SQLite" >> return Wasp.Db.SQLite)
|
|
@ -1,66 +0,0 @@
|
|||||||
module Wasp.Parser.Entity
|
|
||||||
( entity,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Psl.Ast.Model as PslModel
|
|
||||||
import qualified Wasp.Psl.Parser.Model
|
|
||||||
import qualified Wasp.Wasp.Entity as Entity
|
|
||||||
|
|
||||||
entity :: Parser Entity.Entity
|
|
||||||
entity = do
|
|
||||||
_ <- L.reserved L.reservedNameEntity
|
|
||||||
name <- L.identifier
|
|
||||||
_ <- L.symbol "{=psl"
|
|
||||||
pslModelBody <- Wasp.Psl.Parser.Model.body
|
|
||||||
_ <- L.symbol "psl=}"
|
|
||||||
|
|
||||||
return
|
|
||||||
Entity.Entity
|
|
||||||
{ Entity._name = name,
|
|
||||||
Entity._fields = getEntityFields pslModelBody,
|
|
||||||
Entity._pslModelBody = pslModelBody
|
|
||||||
}
|
|
||||||
|
|
||||||
getEntityFields :: PslModel.Body -> [Entity.Field]
|
|
||||||
getEntityFields (PslModel.Body pslElements) = map pslFieldToEntityField pslFields
|
|
||||||
where
|
|
||||||
pslFields = [field | (PslModel.ElementField field) <- pslElements]
|
|
||||||
|
|
||||||
pslFieldToEntityField :: PslModel.Field -> Entity.Field
|
|
||||||
pslFieldToEntityField pslField =
|
|
||||||
Entity.Field
|
|
||||||
{ Entity._fieldName = PslModel._name pslField,
|
|
||||||
Entity._fieldType =
|
|
||||||
pslFieldTypeToEntityFieldType
|
|
||||||
(PslModel._type pslField)
|
|
||||||
(PslModel._typeModifiers pslField)
|
|
||||||
}
|
|
||||||
|
|
||||||
pslFieldTypeToEntityFieldType ::
|
|
||||||
PslModel.FieldType ->
|
|
||||||
[PslModel.FieldTypeModifier] ->
|
|
||||||
Entity.FieldType
|
|
||||||
pslFieldTypeToEntityFieldType fType fTypeModifiers =
|
|
||||||
let scalar = pslFieldTypeToScalar fType
|
|
||||||
in case fTypeModifiers of
|
|
||||||
[] -> Entity.FieldTypeScalar scalar
|
|
||||||
[PslModel.List] -> Entity.FieldTypeComposite $ Entity.List scalar
|
|
||||||
[PslModel.Optional] -> Entity.FieldTypeComposite $ Entity.Optional scalar
|
|
||||||
_ -> error "Not a valid list of modifiers."
|
|
||||||
|
|
||||||
pslFieldTypeToScalar :: PslModel.FieldType -> Entity.Scalar
|
|
||||||
pslFieldTypeToScalar fType = case fType of
|
|
||||||
PslModel.String -> Entity.String
|
|
||||||
PslModel.Boolean -> Entity.Boolean
|
|
||||||
PslModel.Int -> Entity.Int
|
|
||||||
PslModel.BigInt -> Entity.BigInt
|
|
||||||
PslModel.Float -> Entity.Float
|
|
||||||
PslModel.Decimal -> Entity.Decimal
|
|
||||||
PslModel.DateTime -> Entity.DateTime
|
|
||||||
PslModel.Json -> Entity.Json
|
|
||||||
PslModel.Bytes -> Entity.Bytes
|
|
||||||
PslModel.UserType typeName -> Entity.UserType typeName
|
|
||||||
PslModel.Unsupported typeName -> Entity.Unsupported typeName
|
|
@ -1,24 +0,0 @@
|
|||||||
module Wasp.Parser.ExternalCode
|
|
||||||
( extCodeFilePathString,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Path.Posix as PPosix
|
|
||||||
import StrongPath (File', Path, Posix, Rel)
|
|
||||||
import qualified StrongPath.Path as SP.Path
|
|
||||||
import Text.Parsec (unexpected)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import Wasp.AppSpec.ExternalCode (SourceExternalCodeDir)
|
|
||||||
import qualified Wasp.Parser.Common
|
|
||||||
|
|
||||||
-- Parses string literal that is file path to file in source external code dir.
|
|
||||||
-- Returns file path relative to the external code dir.
|
|
||||||
-- Example of input: "@ext/some/file.txt". Output would be: "some/file.txt".
|
|
||||||
extCodeFilePathString :: Parser (Path Posix (Rel SourceExternalCodeDir) File')
|
|
||||||
extCodeFilePathString = do
|
|
||||||
path <- Wasp.Parser.Common.relPosixFilePathString
|
|
||||||
maybe
|
|
||||||
(unexpected $ "string \"" ++ show path ++ "\": External code file path should start with \"@ext/\".")
|
|
||||||
return
|
|
||||||
-- TODO: Once StrongPath supports stripProperPrefix method, use that instead of transforming it to Path and back.
|
|
||||||
(SP.Path.fromPathRelFileP <$> PPosix.stripProperPrefix [PPosix.reldir|@ext|] (SP.Path.toPathRelFileP path))
|
|
@ -1,12 +0,0 @@
|
|||||||
module Wasp.Parser.JsCode
|
|
||||||
( jsCode,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Data.Text as Text
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Parser.Common as P
|
|
||||||
import qualified Wasp.Wasp.JsCode as WJS
|
|
||||||
|
|
||||||
jsCode :: Parser WJS.JsCode
|
|
||||||
jsCode = WJS.JsCode . Text.pack <$> P.waspNamedClosure "js"
|
|
@ -1,32 +0,0 @@
|
|||||||
module Wasp.Parser.JsImport
|
|
||||||
( jsImport,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Text.Parsec ((<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.ExternalCode
|
|
||||||
import qualified Wasp.Wasp.JsImport as Wasp.JsImport
|
|
||||||
|
|
||||||
-- | Parses subset of JS import statement (only default or single named import, and only external code files):
|
|
||||||
-- import <identifier> from "@ext/..."
|
|
||||||
-- import { <identifier> } from "@ext/..."
|
|
||||||
jsImport :: Parser Wasp.JsImport.JsImport
|
|
||||||
jsImport = do
|
|
||||||
L.whiteSpace
|
|
||||||
_ <- L.reserved L.reservedNameImport
|
|
||||||
-- For now we support only default import or one named import.
|
|
||||||
(defaultImport, namedImports) <-
|
|
||||||
((\i -> (Just i, [])) <$> L.identifier)
|
|
||||||
<|> ((\i -> (Nothing, [i])) <$> L.braces L.identifier)
|
|
||||||
_ <- L.reserved L.reservedNameFrom
|
|
||||||
-- TODO: For now we only support double quotes here, we should also support single quotes.
|
|
||||||
-- We would need to write this from scratch, with single quote escaping enabled.
|
|
||||||
from <- Wasp.Parser.ExternalCode.extCodeFilePathString
|
|
||||||
return
|
|
||||||
Wasp.JsImport.JsImport
|
|
||||||
{ Wasp.JsImport._defaultImport = defaultImport,
|
|
||||||
Wasp.JsImport._namedImports = namedImports,
|
|
||||||
Wasp.JsImport._from = from
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
module Wasp.Parser.NpmDependencies
|
|
||||||
( npmDependencies,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Data.Aeson as Aeson
|
|
||||||
import qualified Data.ByteString.Lazy.UTF8 as BLU
|
|
||||||
import qualified Data.HashMap.Strict as M
|
|
||||||
import Text.Parsec (try)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.NpmDependency as ND
|
|
||||||
import qualified Wasp.Parser.Common as P
|
|
||||||
import Wasp.Wasp.NpmDependencies (NpmDependencies)
|
|
||||||
import qualified Wasp.Wasp.NpmDependencies as NpmDependencies
|
|
||||||
|
|
||||||
npmDependencies :: Parser NpmDependencies
|
|
||||||
npmDependencies = try $ do
|
|
||||||
L.reserved L.reservedNameDependencies
|
|
||||||
closureContent <- P.waspNamedClosure "json"
|
|
||||||
let jsonBytestring = BLU.fromString $ "{ " ++ closureContent ++ " }"
|
|
||||||
npmDeps <- case Aeson.eitherDecode' jsonBytestring :: Either String (M.HashMap String String) of
|
|
||||||
Left errorMessage -> fail $ "Failed to parse dependencies JSON: " ++ errorMessage
|
|
||||||
Right rawDeps -> return $ map rawDepToNpmDep (M.toList rawDeps)
|
|
||||||
return
|
|
||||||
NpmDependencies.NpmDependencies
|
|
||||||
{ NpmDependencies._dependencies = npmDeps
|
|
||||||
}
|
|
||||||
where
|
|
||||||
rawDepToNpmDep :: (String, String) -> ND.NpmDependency
|
|
||||||
rawDepToNpmDep (name, version) = ND.NpmDependency {ND._name = name, ND._version = version}
|
|
@ -1,50 +0,0 @@
|
|||||||
module Wasp.Parser.Operation
|
|
||||||
( jsFunctionPropParser,
|
|
||||||
entitiesPropParser,
|
|
||||||
getJsFunctionFromProps,
|
|
||||||
getEntitiesFromProps,
|
|
||||||
getAuthEnabledFromProps,
|
|
||||||
properties,
|
|
||||||
-- FOR TESTS:
|
|
||||||
Property (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (listToMaybe)
|
|
||||||
import Text.Parsec ((<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as C
|
|
||||||
import qualified Wasp.Parser.JsImport
|
|
||||||
import qualified Wasp.Wasp.JsImport as Wasp.JsImport
|
|
||||||
|
|
||||||
data Property
|
|
||||||
= JsFunction !Wasp.JsImport.JsImport
|
|
||||||
| Entities ![String]
|
|
||||||
| AuthEnabled !Bool
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
properties :: Parser [Property]
|
|
||||||
properties =
|
|
||||||
L.commaSep1 $
|
|
||||||
jsFunctionPropParser
|
|
||||||
<|> entitiesPropParser
|
|
||||||
<|> authEnabledPropParser
|
|
||||||
|
|
||||||
jsFunctionPropParser :: Parser Property
|
|
||||||
jsFunctionPropParser = JsFunction <$> C.waspProperty "fn" Wasp.Parser.JsImport.jsImport
|
|
||||||
|
|
||||||
getJsFunctionFromProps :: [Property] -> Maybe Wasp.JsImport.JsImport
|
|
||||||
getJsFunctionFromProps ps = listToMaybe [f | JsFunction f <- ps]
|
|
||||||
|
|
||||||
entitiesPropParser :: Parser Property
|
|
||||||
entitiesPropParser = Entities <$> C.waspProperty "entities" (C.waspList L.identifier)
|
|
||||||
|
|
||||||
getEntitiesFromProps :: [Property] -> Maybe [String]
|
|
||||||
getEntitiesFromProps ps = listToMaybe [es | Entities es <- ps]
|
|
||||||
|
|
||||||
authEnabledPropParser :: Parser Property
|
|
||||||
authEnabledPropParser = AuthEnabled <$> C.waspProperty "auth" L.bool
|
|
||||||
|
|
||||||
getAuthEnabledFromProps :: [Property] -> Maybe Bool
|
|
||||||
getAuthEnabledFromProps ps = listToMaybe [aE | AuthEnabled aE <- ps]
|
|
@ -1,55 +0,0 @@
|
|||||||
module Wasp.Parser.Page
|
|
||||||
( page,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe, listToMaybe)
|
|
||||||
import Text.Parsec
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import Wasp.Lexer
|
|
||||||
import Wasp.Parser.Common
|
|
||||||
import qualified Wasp.Parser.JsImport
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
import qualified Wasp.Wasp.Page as Page
|
|
||||||
|
|
||||||
data PageProperty
|
|
||||||
= Title !String
|
|
||||||
| Component !JsImport
|
|
||||||
| AuthRequired !Bool
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
-- | Parses Page properties, separated by a comma.
|
|
||||||
pageProperties :: Parser [PageProperty]
|
|
||||||
pageProperties =
|
|
||||||
commaSep1 $
|
|
||||||
pagePropertyTitle
|
|
||||||
<|> pagePropertyComponent
|
|
||||||
<|> pagePropertyAuthRequired
|
|
||||||
|
|
||||||
-- NOTE(matija): this is currently unused?
|
|
||||||
pagePropertyTitle :: Parser PageProperty
|
|
||||||
pagePropertyTitle = Title <$> waspPropertyStringLiteral "title"
|
|
||||||
|
|
||||||
pagePropertyComponent :: Parser PageProperty
|
|
||||||
pagePropertyComponent = Component <$> waspProperty "component" Wasp.Parser.JsImport.jsImport
|
|
||||||
|
|
||||||
pagePropertyAuthRequired :: Parser PageProperty
|
|
||||||
pagePropertyAuthRequired = AuthRequired <$> waspPropertyBool "authRequired"
|
|
||||||
|
|
||||||
getPageAuthRequired :: [PageProperty] -> Maybe Bool
|
|
||||||
getPageAuthRequired ps = listToMaybe [a | AuthRequired a <- ps]
|
|
||||||
|
|
||||||
getPageComponent :: [PageProperty] -> Maybe JsImport
|
|
||||||
getPageComponent ps = listToMaybe [c | Component c <- ps]
|
|
||||||
|
|
||||||
-- | Top level parser, parses Page.
|
|
||||||
page :: Parser Page.Page
|
|
||||||
page = do
|
|
||||||
(pageName, pageProps) <- waspElementNameAndClosureContent reservedNamePage pageProperties
|
|
||||||
|
|
||||||
return
|
|
||||||
Page.Page
|
|
||||||
{ Page._name = pageName,
|
|
||||||
Page._component = fromMaybe (error "Page component is missing.") (getPageComponent pageProps),
|
|
||||||
Page._authRequired = getPageAuthRequired pageProps
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
module Wasp.Parser.Query
|
|
||||||
( query,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as C
|
|
||||||
import qualified Wasp.Parser.Operation as Operation
|
|
||||||
import Wasp.Wasp.Query (Query)
|
|
||||||
import qualified Wasp.Wasp.Query as Query
|
|
||||||
|
|
||||||
query :: Parser Query
|
|
||||||
query = do
|
|
||||||
(name, props) <- C.waspElementNameAndClosureContent L.reservedNameQuery Operation.properties
|
|
||||||
return
|
|
||||||
Query.Query
|
|
||||||
{ Query._name = name,
|
|
||||||
Query._jsFunction =
|
|
||||||
fromMaybe (error "Query js function is missing.") (Operation.getJsFunctionFromProps props),
|
|
||||||
Query._entities = Operation.getEntitiesFromProps props,
|
|
||||||
Query._auth = Operation.getAuthEnabledFromProps props
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
module Wasp.Parser.Route
|
|
||||||
( route,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Wasp.Route as Route
|
|
||||||
|
|
||||||
-- | Top level parser, parses route Wasp element.
|
|
||||||
route :: Parser Route.Route
|
|
||||||
route = do
|
|
||||||
-- route "some/url/path"
|
|
||||||
L.reserved L.reservedNameRoute
|
|
||||||
urlPath <- L.stringLiteral
|
|
||||||
|
|
||||||
-- -> page somePage
|
|
||||||
L.reserved "->"
|
|
||||||
L.reserved L.reservedNamePage
|
|
||||||
targetPage <- L.identifier
|
|
||||||
|
|
||||||
return
|
|
||||||
Route.Route
|
|
||||||
{ Route._urlPath = urlPath,
|
|
||||||
Route._targetPage = targetPage
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
module Wasp.Parser.Server
|
|
||||||
( server,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Maybe (fromMaybe, listToMaybe)
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Lexer as L
|
|
||||||
import qualified Wasp.Parser.Common as C
|
|
||||||
import qualified Wasp.Parser.JsImport
|
|
||||||
import qualified Wasp.Wasp.JsImport as Wasp.JsImport
|
|
||||||
import Wasp.Wasp.Server (Server)
|
|
||||||
import qualified Wasp.Wasp.Server as Server
|
|
||||||
|
|
||||||
server :: Parser Server
|
|
||||||
server = do
|
|
||||||
L.reserved L.reservedNameServer
|
|
||||||
props <- C.waspClosure properties
|
|
||||||
|
|
||||||
return
|
|
||||||
Server.Server
|
|
||||||
{ Server._setupJsFunction =
|
|
||||||
fromMaybe (error "Server js function is missing.") (getSetupJsFunctionFromProps props)
|
|
||||||
}
|
|
||||||
|
|
||||||
data Property
|
|
||||||
= SetupJsFunction !Wasp.JsImport.JsImport
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
properties :: Parser [Property]
|
|
||||||
properties =
|
|
||||||
L.commaSep1 setupJsFunctionPropParser
|
|
||||||
|
|
||||||
setupJsFunctionPropParser :: Parser Property
|
|
||||||
setupJsFunctionPropParser = SetupJsFunction <$> C.waspProperty "setupFn" Wasp.Parser.JsImport.jsImport
|
|
||||||
|
|
||||||
getSetupJsFunctionFromProps :: [Property] -> Maybe Wasp.JsImport.JsImport
|
|
||||||
getSetupJsFunctionFromProps ps = listToMaybe [f | SetupJsFunction f <- ps]
|
|
@ -1,20 +0,0 @@
|
|||||||
module Wasp.Parser.Style
|
|
||||||
( style,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import qualified Data.Text as Text
|
|
||||||
import Text.Parsec ((<|>))
|
|
||||||
import Text.Parsec.String (Parser)
|
|
||||||
import qualified Wasp.Parser.Common
|
|
||||||
import qualified Wasp.Parser.ExternalCode
|
|
||||||
import qualified Wasp.Wasp.Style as Wasp.Style
|
|
||||||
|
|
||||||
style :: Parser Wasp.Style.Style
|
|
||||||
style = cssFile <|> cssCode
|
|
||||||
|
|
||||||
cssFile :: Parser Wasp.Style.Style
|
|
||||||
cssFile = Wasp.Style.ExtCodeCssFile <$> Wasp.Parser.ExternalCode.extCodeFilePathString
|
|
||||||
|
|
||||||
cssCode :: Parser Wasp.Style.Style
|
|
||||||
cssCode = Wasp.Style.CssCode . Text.pack <$> Wasp.Parser.Common.waspNamedClosure "css"
|
|
@ -8,6 +8,8 @@ module Wasp.Util
|
|||||||
indent,
|
indent,
|
||||||
concatShortPrefixAndText,
|
concatShortPrefixAndText,
|
||||||
concatPrefixAndText,
|
concatPrefixAndText,
|
||||||
|
insertAt,
|
||||||
|
leftPad,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
@ -102,3 +104,17 @@ concatShortPrefixAndText prefix text =
|
|||||||
concatPrefixAndText :: String -> String -> String
|
concatPrefixAndText :: String -> String -> String
|
||||||
concatPrefixAndText prefix text =
|
concatPrefixAndText prefix text =
|
||||||
if length (lines text) <= 1 then prefix ++ text else prefix ++ "\n" ++ indent 2 text
|
if length (lines text) <= 1 then prefix ++ text else prefix ++ "\n" ++ indent 2 text
|
||||||
|
|
||||||
|
-- | Adds given element to the start of the given list until the list is of specified length.
|
||||||
|
-- leftPad ' ' 4 "hi" == " hi"
|
||||||
|
-- leftPad ' ' 4 "hihihi" == "hihihi"
|
||||||
|
leftPad :: a -> Int -> [a] -> [a]
|
||||||
|
leftPad padElem n list = replicate (max 0 (n - length list)) padElem ++ list
|
||||||
|
|
||||||
|
-- | Inserts a given @theInsert@ list into the given @host@ list so that @theInsert@
|
||||||
|
-- starts at index @idx@ in the @host@.
|
||||||
|
-- Example: @insertAt "hi" 2 "hoho" == "hohiho"@
|
||||||
|
insertAt :: [a] -> Int -> [a] -> [a]
|
||||||
|
insertAt theInsert idx host =
|
||||||
|
let (before, after) = splitAt idx host
|
||||||
|
in before ++ theInsert ++ after
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
module Wasp.Util.Terminal
|
module Wasp.Util.Terminal
|
||||||
( Style (..),
|
( Style (..),
|
||||||
applyStyles,
|
applyStyles,
|
||||||
|
styleCode,
|
||||||
|
escapeCode,
|
||||||
|
resetCode,
|
||||||
)
|
)
|
||||||
where
|
where
|
||||||
|
|
||||||
|
@ -1,259 +0,0 @@
|
|||||||
module Wasp.Wasp
|
|
||||||
( Wasp,
|
|
||||||
WaspElement (..),
|
|
||||||
fromWaspElems,
|
|
||||||
module Wasp.Wasp.JsImport,
|
|
||||||
getJsImports,
|
|
||||||
setJsImports,
|
|
||||||
module Wasp.Wasp.App,
|
|
||||||
fromApp,
|
|
||||||
getApp,
|
|
||||||
setApp,
|
|
||||||
getAuth,
|
|
||||||
getServer,
|
|
||||||
getPSLEntities,
|
|
||||||
getDb,
|
|
||||||
module Wasp.Wasp.Page,
|
|
||||||
getPages,
|
|
||||||
addPage,
|
|
||||||
getRoutes,
|
|
||||||
getQueries,
|
|
||||||
addQuery,
|
|
||||||
getQueryByName,
|
|
||||||
getActions,
|
|
||||||
addAction,
|
|
||||||
getActionByName,
|
|
||||||
setExternalCodeFiles,
|
|
||||||
getExternalCodeFiles,
|
|
||||||
setDotEnvFile,
|
|
||||||
getDotEnvFile,
|
|
||||||
setMigrationsDir,
|
|
||||||
getMigrationsDir,
|
|
||||||
setIsBuild,
|
|
||||||
getIsBuild,
|
|
||||||
setNpmDependencies,
|
|
||||||
getNpmDependencies,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import StrongPath (Abs, Dir, File', Path')
|
|
||||||
import qualified Wasp.AppSpec.ExternalCode as ExternalCode
|
|
||||||
import Wasp.Common (DbMigrationsDir)
|
|
||||||
import qualified Wasp.Util as U
|
|
||||||
import qualified Wasp.Wasp.Action as Wasp.Action
|
|
||||||
import Wasp.Wasp.App
|
|
||||||
import qualified Wasp.Wasp.Auth as Wasp.Auth
|
|
||||||
import qualified Wasp.Wasp.Db as Wasp.Db
|
|
||||||
import Wasp.Wasp.Entity
|
|
||||||
import Wasp.Wasp.JsImport
|
|
||||||
import Wasp.Wasp.NpmDependencies (NpmDependencies)
|
|
||||||
import qualified Wasp.Wasp.NpmDependencies as Wasp.NpmDependencies
|
|
||||||
import Wasp.Wasp.Page
|
|
||||||
import qualified Wasp.Wasp.Query as Wasp.Query
|
|
||||||
import Wasp.Wasp.Route
|
|
||||||
import qualified Wasp.Wasp.Server as Wasp.Server
|
|
||||||
|
|
||||||
-- * Wasp
|
|
||||||
|
|
||||||
data Wasp = Wasp
|
|
||||||
{ waspElements :: [WaspElement],
|
|
||||||
waspJsImports :: [JsImport],
|
|
||||||
externalCodeFiles :: [ExternalCode.File],
|
|
||||||
dotEnvFile :: Maybe (Path' Abs File'),
|
|
||||||
migrationsDir :: Maybe (Path' Abs (Dir DbMigrationsDir)),
|
|
||||||
isBuild :: Bool
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data WaspElement
|
|
||||||
= WaspElementApp !App
|
|
||||||
| WaspElementAuth !Wasp.Auth.Auth
|
|
||||||
| WaspElementDb !Wasp.Db.Db
|
|
||||||
| WaspElementPage !Page
|
|
||||||
| WaspElementNpmDependencies !NpmDependencies
|
|
||||||
| WaspElementRoute !Route
|
|
||||||
| WaspElementEntity !Wasp.Wasp.Entity.Entity
|
|
||||||
| WaspElementQuery !Wasp.Query.Query
|
|
||||||
| WaspElementAction !Wasp.Action.Action
|
|
||||||
| WaspElementServer !Wasp.Server.Server
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
fromWaspElems :: [WaspElement] -> Wasp
|
|
||||||
fromWaspElems elems =
|
|
||||||
Wasp
|
|
||||||
{ waspElements = elems,
|
|
||||||
waspJsImports = [],
|
|
||||||
externalCodeFiles = [],
|
|
||||||
dotEnvFile = Nothing,
|
|
||||||
migrationsDir = Nothing,
|
|
||||||
isBuild = False
|
|
||||||
}
|
|
||||||
|
|
||||||
-- * Build
|
|
||||||
|
|
||||||
getIsBuild :: Wasp -> Bool
|
|
||||||
getIsBuild = isBuild
|
|
||||||
|
|
||||||
setIsBuild :: Wasp -> Bool -> Wasp
|
|
||||||
setIsBuild wasp isBuildNew = wasp {isBuild = isBuildNew}
|
|
||||||
|
|
||||||
-- * External code files
|
|
||||||
|
|
||||||
getExternalCodeFiles :: Wasp -> [ExternalCode.File]
|
|
||||||
getExternalCodeFiles = externalCodeFiles
|
|
||||||
|
|
||||||
setExternalCodeFiles :: Wasp -> [ExternalCode.File] -> Wasp
|
|
||||||
setExternalCodeFiles wasp files = wasp {externalCodeFiles = files}
|
|
||||||
|
|
||||||
-- * Dot env files
|
|
||||||
|
|
||||||
getDotEnvFile :: Wasp -> Maybe (Path' Abs File')
|
|
||||||
getDotEnvFile = dotEnvFile
|
|
||||||
|
|
||||||
setDotEnvFile :: Wasp -> Maybe (Path' Abs File') -> Wasp
|
|
||||||
setDotEnvFile wasp file = wasp {dotEnvFile = file}
|
|
||||||
|
|
||||||
-- * Migrations dir
|
|
||||||
|
|
||||||
getMigrationsDir :: Wasp -> Maybe (Path' Abs (Dir DbMigrationsDir))
|
|
||||||
getMigrationsDir = migrationsDir
|
|
||||||
|
|
||||||
setMigrationsDir :: Wasp -> Maybe (Path' Abs (Dir DbMigrationsDir)) -> Wasp
|
|
||||||
setMigrationsDir wasp dir = wasp {migrationsDir = dir}
|
|
||||||
|
|
||||||
-- * Js imports
|
|
||||||
|
|
||||||
getJsImports :: Wasp -> [JsImport]
|
|
||||||
getJsImports = waspJsImports
|
|
||||||
|
|
||||||
setJsImports :: Wasp -> [JsImport] -> Wasp
|
|
||||||
setJsImports wasp jsImports = wasp {waspJsImports = jsImports}
|
|
||||||
|
|
||||||
-- * App
|
|
||||||
|
|
||||||
getApp :: Wasp -> App
|
|
||||||
getApp wasp =
|
|
||||||
let apps = getApps wasp
|
|
||||||
in if length apps /= 1
|
|
||||||
then error "Wasp has to contain exactly one WaspElementApp element!"
|
|
||||||
else head apps
|
|
||||||
|
|
||||||
isAppElem :: WaspElement -> Bool
|
|
||||||
isAppElem WaspElementApp {} = True
|
|
||||||
isAppElem _ = False
|
|
||||||
|
|
||||||
getApps :: Wasp -> [App]
|
|
||||||
getApps wasp = [app | (WaspElementApp app) <- waspElements wasp]
|
|
||||||
|
|
||||||
setApp :: Wasp -> App -> Wasp
|
|
||||||
setApp wasp app = wasp {waspElements = WaspElementApp app : filter (not . isAppElem) (waspElements wasp)}
|
|
||||||
|
|
||||||
fromApp :: App -> Wasp
|
|
||||||
fromApp app = fromWaspElems [WaspElementApp app]
|
|
||||||
|
|
||||||
-- * Auth
|
|
||||||
|
|
||||||
getAuth :: Wasp -> Maybe Wasp.Auth.Auth
|
|
||||||
getAuth wasp =
|
|
||||||
let auths = [a | WaspElementAuth a <- waspElements wasp]
|
|
||||||
in case auths of
|
|
||||||
[] -> Nothing
|
|
||||||
[a] -> Just a
|
|
||||||
_ -> error "Wasp can't contain more than one WaspElementAuth element!"
|
|
||||||
|
|
||||||
-- * Db
|
|
||||||
|
|
||||||
getDb :: Wasp -> Maybe Wasp.Db.Db
|
|
||||||
getDb wasp =
|
|
||||||
let dbs = [db | WaspElementDb db <- waspElements wasp]
|
|
||||||
in case dbs of
|
|
||||||
[] -> Nothing
|
|
||||||
[db] -> Just db
|
|
||||||
_ -> error "Wasp can't contain more than one Db element!"
|
|
||||||
|
|
||||||
-- * NpmDependencies
|
|
||||||
|
|
||||||
getNpmDependencies :: Wasp -> NpmDependencies
|
|
||||||
getNpmDependencies wasp =
|
|
||||||
let depses = [d | (WaspElementNpmDependencies d) <- waspElements wasp]
|
|
||||||
in case depses of
|
|
||||||
[] -> Wasp.NpmDependencies.empty
|
|
||||||
[deps] -> deps
|
|
||||||
_ -> error "Wasp can't contain more than one NpmDependencies element!"
|
|
||||||
|
|
||||||
isNpmDependenciesElem :: WaspElement -> Bool
|
|
||||||
isNpmDependenciesElem WaspElementNpmDependencies {} = True
|
|
||||||
isNpmDependenciesElem _ = False
|
|
||||||
|
|
||||||
setNpmDependencies :: Wasp -> NpmDependencies -> Wasp
|
|
||||||
setNpmDependencies wasp deps =
|
|
||||||
wasp
|
|
||||||
{ waspElements = WaspElementNpmDependencies deps : filter (not . isNpmDependenciesElem) (waspElements wasp)
|
|
||||||
}
|
|
||||||
|
|
||||||
-- * Routes
|
|
||||||
|
|
||||||
getRoutes :: Wasp -> [Route]
|
|
||||||
getRoutes wasp = [route | (WaspElementRoute route) <- waspElements wasp]
|
|
||||||
|
|
||||||
-- * Pages
|
|
||||||
|
|
||||||
getPages :: Wasp -> [Page]
|
|
||||||
getPages wasp = [page | (WaspElementPage page) <- waspElements wasp]
|
|
||||||
|
|
||||||
addPage :: Wasp -> Page -> Wasp
|
|
||||||
addPage wasp page = wasp {waspElements = WaspElementPage page : waspElements wasp}
|
|
||||||
|
|
||||||
-- * Query
|
|
||||||
|
|
||||||
getQueries :: Wasp -> [Wasp.Query.Query]
|
|
||||||
getQueries wasp = [query | (WaspElementQuery query) <- waspElements wasp]
|
|
||||||
|
|
||||||
addQuery :: Wasp -> Wasp.Query.Query -> Wasp
|
|
||||||
addQuery wasp query = wasp {waspElements = WaspElementQuery query : waspElements wasp}
|
|
||||||
|
|
||||||
-- | Gets query with a specified name from wasp, if such an action exists.
|
|
||||||
-- We assume here that there are no two queries with same name.
|
|
||||||
getQueryByName :: Wasp -> String -> Maybe Wasp.Query.Query
|
|
||||||
getQueryByName wasp name = U.headSafe $ filter (\a -> Wasp.Query._name a == name) (getQueries wasp)
|
|
||||||
|
|
||||||
-- * Action
|
|
||||||
|
|
||||||
getActions :: Wasp -> [Wasp.Action.Action]
|
|
||||||
getActions wasp = [action | (WaspElementAction action) <- waspElements wasp]
|
|
||||||
|
|
||||||
addAction :: Wasp -> Wasp.Action.Action -> Wasp
|
|
||||||
addAction wasp action = wasp {waspElements = WaspElementAction action : waspElements wasp}
|
|
||||||
|
|
||||||
-- | Gets action with a specified name from wasp, if such an action exists.
|
|
||||||
-- We assume here that there are no two actions with same name.
|
|
||||||
getActionByName :: Wasp -> String -> Maybe Wasp.Action.Action
|
|
||||||
getActionByName wasp name = U.headSafe $ filter (\a -> Wasp.Action._name a == name) (getActions wasp)
|
|
||||||
|
|
||||||
-- * Entities
|
|
||||||
|
|
||||||
getPSLEntities :: Wasp -> [Wasp.Wasp.Entity.Entity]
|
|
||||||
getPSLEntities wasp = [entity | (WaspElementEntity entity) <- waspElements wasp]
|
|
||||||
|
|
||||||
-- * Get server
|
|
||||||
|
|
||||||
getServer :: Wasp -> Maybe Wasp.Server.Server
|
|
||||||
getServer wasp =
|
|
||||||
let servers = [s | WaspElementServer s <- waspElements wasp]
|
|
||||||
in case servers of
|
|
||||||
[] -> Nothing
|
|
||||||
[s] -> Just s
|
|
||||||
_ -> error "Wasp can't contain more than one WaspElementAuth element!"
|
|
||||||
|
|
||||||
-- * ToJSON instances.
|
|
||||||
|
|
||||||
instance ToJSON Wasp where
|
|
||||||
toJSON wasp =
|
|
||||||
object
|
|
||||||
[ "app" .= getApp wasp,
|
|
||||||
"pages" .= getPages wasp,
|
|
||||||
"routes" .= getRoutes wasp,
|
|
||||||
"jsImports" .= getJsImports wasp,
|
|
||||||
"server" .= getServer wasp
|
|
||||||
]
|
|
@ -1,26 +0,0 @@
|
|||||||
module Wasp.Wasp.Action
|
|
||||||
( Action (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
|
|
||||||
-- TODO: Very similar to Wasp.Query, consider extracting duplication.
|
|
||||||
|
|
||||||
data Action = Action
|
|
||||||
{ _name :: !String,
|
|
||||||
_jsFunction :: !JsImport,
|
|
||||||
_entities :: !(Maybe [String]),
|
|
||||||
_auth :: !(Maybe Bool)
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Action where
|
|
||||||
toJSON action =
|
|
||||||
object
|
|
||||||
[ "name" .= _name action,
|
|
||||||
"jsFunction" .= _jsFunction action,
|
|
||||||
"entities" .= _entities action,
|
|
||||||
"auth" .= _auth action
|
|
||||||
]
|
|
@ -1,20 +0,0 @@
|
|||||||
module Wasp.Wasp.App
|
|
||||||
( App (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
|
|
||||||
data App = App
|
|
||||||
{ appName :: !String, -- Identifier
|
|
||||||
appTitle :: !String,
|
|
||||||
appHead :: !(Maybe [String])
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON App where
|
|
||||||
toJSON app =
|
|
||||||
object
|
|
||||||
[ "name" .= appName app,
|
|
||||||
"title" .= appTitle app
|
|
||||||
]
|
|
@ -1,17 +0,0 @@
|
|||||||
module Wasp.Wasp.Auth
|
|
||||||
( Auth (..),
|
|
||||||
AuthMethod (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
data Auth = Auth
|
|
||||||
{ _userEntity :: !String,
|
|
||||||
_methods :: [AuthMethod],
|
|
||||||
_onAuthFailedRedirectTo :: !String,
|
|
||||||
_onAuthSucceededRedirectTo :: !String
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data AuthMethod
|
|
||||||
= EmailAndPassword
|
|
||||||
deriving (Show, Eq)
|
|
@ -1,15 +0,0 @@
|
|||||||
module Wasp.Wasp.Db
|
|
||||||
( Db (..),
|
|
||||||
DbSystem (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
data Db = Db
|
|
||||||
{ _system :: !DbSystem
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data DbSystem
|
|
||||||
= PostgreSQL
|
|
||||||
| SQLite
|
|
||||||
deriving (Show, Eq)
|
|
@ -1,55 +0,0 @@
|
|||||||
module Wasp.Wasp.Entity
|
|
||||||
( Entity (..),
|
|
||||||
Field (..),
|
|
||||||
FieldType (..),
|
|
||||||
Scalar (..),
|
|
||||||
Composite (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import qualified Wasp.Psl.Ast.Model
|
|
||||||
|
|
||||||
data Entity = Entity
|
|
||||||
{ _name :: !String,
|
|
||||||
_fields :: ![Field],
|
|
||||||
_pslModelBody :: !Wasp.Psl.Ast.Model.Body
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data Field = Field
|
|
||||||
{ _fieldName :: !String,
|
|
||||||
_fieldType :: !FieldType
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data FieldType = FieldTypeScalar Scalar | FieldTypeComposite Composite
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data Composite = Optional Scalar | List Scalar
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
data Scalar
|
|
||||||
= String
|
|
||||||
| Boolean
|
|
||||||
| Int
|
|
||||||
| BigInt
|
|
||||||
| Float
|
|
||||||
| Decimal
|
|
||||||
| DateTime
|
|
||||||
| Json
|
|
||||||
| Bytes
|
|
||||||
| -- | Name of the user-defined type.
|
|
||||||
-- This could be another entity, or maybe an enum,
|
|
||||||
-- we don't know here yet.
|
|
||||||
UserType String
|
|
||||||
| Unsupported String
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Entity where
|
|
||||||
toJSON entity =
|
|
||||||
object
|
|
||||||
[ "name" .= _name entity,
|
|
||||||
"fields" .= show (_fields entity),
|
|
||||||
"pslModelBody" .= show (_pslModelBody entity)
|
|
||||||
]
|
|
@ -1,15 +0,0 @@
|
|||||||
module Wasp.Wasp.JsCode
|
|
||||||
( JsCode (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..))
|
|
||||||
import Data.Text (Text)
|
|
||||||
|
|
||||||
data JsCode = JsCode !Text deriving (Show, Eq)
|
|
||||||
|
|
||||||
-- TODO(matija): Currently generator is relying on this implementation, which is not
|
|
||||||
-- ideal. Ideally all the generation logic would be in the generator. But for now this was
|
|
||||||
-- the simplest way to implement it.
|
|
||||||
instance ToJSON JsCode where
|
|
||||||
toJSON (JsCode code) = toJSON code
|
|
@ -1,25 +0,0 @@
|
|||||||
module Wasp.Wasp.JsImport
|
|
||||||
( JsImport (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import StrongPath (File', Path, Posix, Rel)
|
|
||||||
import qualified StrongPath as SP
|
|
||||||
import Wasp.AppSpec.ExternalCode (SourceExternalCodeDir)
|
|
||||||
|
|
||||||
-- | Represents javascript import -> "import <what> from <from>".
|
|
||||||
data JsImport = JsImport
|
|
||||||
{ _defaultImport :: !(Maybe String),
|
|
||||||
_namedImports :: ![String],
|
|
||||||
_from :: Path Posix (Rel SourceExternalCodeDir) File'
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON JsImport where
|
|
||||||
toJSON jsImport =
|
|
||||||
object
|
|
||||||
[ "defaultImport" .= _defaultImport jsImport,
|
|
||||||
"namedImports" .= _namedImports jsImport,
|
|
||||||
"from" .= SP.fromRelFileP (_from jsImport)
|
|
||||||
]
|
|
@ -1,22 +0,0 @@
|
|||||||
module Wasp.Wasp.NpmDependencies
|
|
||||||
( NpmDependencies (..),
|
|
||||||
empty,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import Wasp.NpmDependency
|
|
||||||
|
|
||||||
data NpmDependencies = NpmDependencies
|
|
||||||
{ _dependencies :: ![NpmDependency]
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
empty :: NpmDependencies
|
|
||||||
empty = NpmDependencies {_dependencies = []}
|
|
||||||
|
|
||||||
instance ToJSON NpmDependencies where
|
|
||||||
toJSON deps =
|
|
||||||
object
|
|
||||||
[ "dependencies" .= _dependencies deps
|
|
||||||
]
|
|
@ -1,38 +0,0 @@
|
|||||||
module Wasp.Wasp.Operation
|
|
||||||
( Operation (..),
|
|
||||||
getName,
|
|
||||||
getJsFn,
|
|
||||||
getEntities,
|
|
||||||
getAuth,
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
-- TODO: Is this ok approach, should I instead use typeclass?
|
|
||||||
-- So far, all usages in the codebase could be easily replaced with the Typeclass.
|
|
||||||
|
|
||||||
import Wasp.Wasp.Action (Action)
|
|
||||||
import qualified Wasp.Wasp.Action as Action
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
import Wasp.Wasp.Query (Query)
|
|
||||||
import qualified Wasp.Wasp.Query as Query
|
|
||||||
|
|
||||||
data Operation
|
|
||||||
= QueryOp Query
|
|
||||||
| ActionOp Action
|
|
||||||
deriving (Show)
|
|
||||||
|
|
||||||
getName :: Operation -> String
|
|
||||||
getName (QueryOp query) = Query._name query
|
|
||||||
getName (ActionOp action) = Action._name action
|
|
||||||
|
|
||||||
getJsFn :: Operation -> JsImport
|
|
||||||
getJsFn (QueryOp query) = Query._jsFunction query
|
|
||||||
getJsFn (ActionOp action) = Action._jsFunction action
|
|
||||||
|
|
||||||
getEntities :: Operation -> Maybe [String]
|
|
||||||
getEntities (QueryOp query) = Query._entities query
|
|
||||||
getEntities (ActionOp action) = Action._entities action
|
|
||||||
|
|
||||||
getAuth :: Operation -> Maybe Bool
|
|
||||||
getAuth (QueryOp query) = Query._auth query
|
|
||||||
getAuth (ActionOp action) = Action._auth action
|
|
@ -1,21 +0,0 @@
|
|||||||
module Wasp.Wasp.Page
|
|
||||||
( Page (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
|
|
||||||
data Page = Page
|
|
||||||
{ _name :: !String,
|
|
||||||
_component :: !JsImport,
|
|
||||||
_authRequired :: Maybe Bool
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Page where
|
|
||||||
toJSON page =
|
|
||||||
object
|
|
||||||
[ "name" .= _name page,
|
|
||||||
"component" .= _component page
|
|
||||||
]
|
|
@ -1,26 +0,0 @@
|
|||||||
module Wasp.Wasp.Query
|
|
||||||
( Query (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
|
|
||||||
-- TODO: Very similar to Wasp.Action, consider extracting duplication.
|
|
||||||
|
|
||||||
data Query = Query
|
|
||||||
{ _name :: !String,
|
|
||||||
_jsFunction :: !JsImport,
|
|
||||||
_entities :: !(Maybe [String]),
|
|
||||||
_auth :: !(Maybe Bool)
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Query where
|
|
||||||
toJSON query =
|
|
||||||
object
|
|
||||||
[ "name" .= _name query,
|
|
||||||
"jsFunction" .= _jsFunction query,
|
|
||||||
"entities" .= _entities query,
|
|
||||||
"auth" .= _auth query
|
|
||||||
]
|
|
@ -1,21 +0,0 @@
|
|||||||
module Wasp.Wasp.Route
|
|
||||||
( Route (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
|
|
||||||
data Route = Route
|
|
||||||
{ _urlPath :: !String,
|
|
||||||
-- NOTE(matija): for now page is the only possible target, but in
|
|
||||||
-- the future there might be different types of targets (e.g. another route).
|
|
||||||
_targetPage :: !String
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Route where
|
|
||||||
toJSON route =
|
|
||||||
object
|
|
||||||
[ "urlPath" .= _urlPath route,
|
|
||||||
"targetPage" .= _targetPage route
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
module Wasp.Wasp.Server
|
|
||||||
( Server (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..), object, (.=))
|
|
||||||
import Wasp.Wasp.JsImport (JsImport)
|
|
||||||
|
|
||||||
data Server = Server
|
|
||||||
{ _setupJsFunction :: !JsImport
|
|
||||||
}
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Server where
|
|
||||||
toJSON server =
|
|
||||||
object
|
|
||||||
[ "setupJsFunction" .= _setupJsFunction server
|
|
||||||
]
|
|
@ -1,18 +0,0 @@
|
|||||||
module Wasp.Wasp.Style
|
|
||||||
( Style (..),
|
|
||||||
)
|
|
||||||
where
|
|
||||||
|
|
||||||
import Data.Aeson (ToJSON (..))
|
|
||||||
import Data.Text (Text)
|
|
||||||
import StrongPath (File', Path, Posix, Rel, toFilePath)
|
|
||||||
import Wasp.AppSpec.ExternalCode (SourceExternalCodeDir)
|
|
||||||
|
|
||||||
data Style
|
|
||||||
= ExtCodeCssFile !(Path Posix (Rel SourceExternalCodeDir) File')
|
|
||||||
| CssCode !Text
|
|
||||||
deriving (Show, Eq)
|
|
||||||
|
|
||||||
instance ToJSON Style where
|
|
||||||
toJSON (ExtCodeCssFile path) = toJSON $ toFilePath path
|
|
||||||
toJSON (CssCode code) = toJSON code
|
|
@ -1 +1 @@
|
|||||||
resolver: lts-18.15
|
resolver: lts-18.21
|
||||||
|
@ -2,8 +2,8 @@ resolver: ./stack-snapshot.yaml
|
|||||||
packages:
|
packages:
|
||||||
- .
|
- .
|
||||||
extra-deps:
|
extra-deps:
|
||||||
- strong-path-1.1.0.0
|
- strong-path-1.1.2.0
|
||||||
- path-0.9.0
|
- path-0.9.2
|
||||||
- path-io-1.6.3
|
- path-io-1.6.3
|
||||||
# (Martin): I added this per instructions from haskell-language-server, in order to
|
# (Martin): I added this per instructions from haskell-language-server, in order to
|
||||||
# enable type information and documentation on hover for dependencies.
|
# enable type information and documentation on hover for dependencies.
|
||||||
|
@ -8,6 +8,8 @@ module Analyzer.EvaluatorTest where
|
|||||||
|
|
||||||
import Data.Data (Data)
|
import Data.Data (Data)
|
||||||
import Data.List.Split (splitOn)
|
import Data.List.Split (splitOn)
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
|
import qualified StrongPath as SP
|
||||||
import Test.Tasty.Hspec
|
import Test.Tasty.Hspec
|
||||||
import Text.Read (readMaybe)
|
import Text.Read (readMaybe)
|
||||||
import Wasp.Analyzer.Evaluator
|
import Wasp.Analyzer.Evaluator
|
||||||
@ -182,7 +184,7 @@ spec_Evaluator = do
|
|||||||
let typeDefs = TD.addDeclType @Special $ TD.empty
|
let typeDefs = TD.addDeclType @Special $ TD.empty
|
||||||
let source =
|
let source =
|
||||||
[ "special Test {",
|
[ "special Test {",
|
||||||
" imps: [import { field } from \"main.js\", import main from \"main.js\"],",
|
" imps: [import { field } from \"@ext/main.js\", import main from \"@ext/main.js\"],",
|
||||||
" json: {=json \"key\": 1 json=}",
|
" json: {=json \"key\": 1 json=}",
|
||||||
"}"
|
"}"
|
||||||
]
|
]
|
||||||
@ -191,8 +193,8 @@ spec_Evaluator = do
|
|||||||
`shouldBe` Right
|
`shouldBe` Right
|
||||||
[ ( "Test",
|
[ ( "Test",
|
||||||
Special
|
Special
|
||||||
[ ExtImport (ExtImportField "field") "main.js",
|
[ ExtImport (ExtImportField "field") (fromJust $ SP.parseRelFileP "main.js"),
|
||||||
ExtImport (ExtImportModule "main") "main.js"
|
ExtImport (ExtImportModule "main") (fromJust $ SP.parseRelFileP "main.js")
|
||||||
]
|
]
|
||||||
(JSON " \"key\": 1 ")
|
(JSON " \"key\": 1 ")
|
||||||
)
|
)
|
||||||
|
@ -19,14 +19,15 @@ spec_Parser = do
|
|||||||
" yes: true,",
|
" yes: true,",
|
||||||
" no: false,",
|
" no: false,",
|
||||||
" ident: Wasp,",
|
" ident: Wasp,",
|
||||||
|
" // This is a comment",
|
||||||
" innerDict: { innerDictReal: 2.17 }",
|
" innerDict: { innerDictReal: 2.17 }",
|
||||||
"}"
|
"}"
|
||||||
]
|
]
|
||||||
let ast =
|
let ast =
|
||||||
AST
|
AST
|
||||||
[ wctx (1, 1) (10, 1) $
|
[ wctx (1, 1) (11, 1) $
|
||||||
Decl "test" "Decl" $
|
Decl "test" "Decl" $
|
||||||
wctx (1, 11) (10, 1) $
|
wctx (1, 11) (11, 1) $
|
||||||
Dict
|
Dict
|
||||||
[ ("string", wctx (2, 11) (2, 25) $ StringLiteral "Hello Wasp =}"),
|
[ ("string", wctx (2, 11) (2, 25) $ StringLiteral "Hello Wasp =}"),
|
||||||
("escapedString", wctx (3, 18) (3, 29) $ StringLiteral "Look, a \""),
|
("escapedString", wctx (3, 18) (3, 29) $ StringLiteral "Look, a \""),
|
||||||
@ -36,15 +37,32 @@ spec_Parser = do
|
|||||||
("no", wctx (7, 7) (7, 11) $ BoolLiteral False),
|
("no", wctx (7, 7) (7, 11) $ BoolLiteral False),
|
||||||
("ident", wctx (8, 10) (8, 13) $ Var "Wasp"),
|
("ident", wctx (8, 10) (8, 13) $ Var "Wasp"),
|
||||||
( "innerDict",
|
( "innerDict",
|
||||||
wctx (9, 14) (9, 36) $
|
wctx (10, 14) (10, 36) $
|
||||||
Dict
|
Dict
|
||||||
[ ("innerDictReal", wctx (9, 31) (9, 34) $ DoubleLiteral 2.17)
|
[ ("innerDictReal", wctx (10, 31) (10, 34) $ DoubleLiteral 2.17)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
parse source `shouldBe` Right ast
|
parse source `shouldBe` Right ast
|
||||||
|
|
||||||
|
it "Parses comments" $ do
|
||||||
|
let source =
|
||||||
|
unlines
|
||||||
|
[ " // This is some // comment",
|
||||||
|
"/* comment",
|
||||||
|
" span//ning",
|
||||||
|
"multi/*ple lines */",
|
||||||
|
"test /* *hi* */ Decl 42 // One more comment",
|
||||||
|
"// And here is final comment"
|
||||||
|
]
|
||||||
|
let ast =
|
||||||
|
AST
|
||||||
|
[ wctx (5, 1) (5, 23) $
|
||||||
|
Decl "test" "Decl" $ wctx (5, 22) (5, 23) $ IntegerLiteral 42
|
||||||
|
]
|
||||||
|
parse source `shouldBe` Right ast
|
||||||
|
|
||||||
it "Parses external imports" $ do
|
it "Parses external imports" $ do
|
||||||
let source =
|
let source =
|
||||||
unlines
|
unlines
|
||||||
|
@ -5,6 +5,8 @@ module AnalyzerTest where
|
|||||||
import Analyzer.TestUtil (ctx)
|
import Analyzer.TestUtil (ctx)
|
||||||
import Data.Either (isRight)
|
import Data.Either (isRight)
|
||||||
import Data.List (intercalate)
|
import Data.List (intercalate)
|
||||||
|
import Data.Maybe (fromJust)
|
||||||
|
import qualified StrongPath as SP
|
||||||
import Test.Tasty.Hspec
|
import Test.Tasty.Hspec
|
||||||
import Wasp.Analyzer
|
import Wasp.Analyzer
|
||||||
import Wasp.Analyzer.Parser (Ctx)
|
import Wasp.Analyzer.Parser (Ctx)
|
||||||
@ -103,7 +105,11 @@ spec_Analyzer = do
|
|||||||
App.server =
|
App.server =
|
||||||
Just
|
Just
|
||||||
Server.Server
|
Server.Server
|
||||||
{ Server.setupFn = Just $ ExtImport (ExtImportField "setupServer") "@ext/bar.js"
|
{ Server.setupFn =
|
||||||
|
Just $
|
||||||
|
ExtImport
|
||||||
|
(ExtImportField "setupServer")
|
||||||
|
(fromJust $ SP.parseRelFileP "bar.js")
|
||||||
},
|
},
|
||||||
App.db = Just Db.Db {Db.system = Just Db.PostgreSQL}
|
App.db = Just Db.Db {Db.system = Just Db.PostgreSQL}
|
||||||
}
|
}
|
||||||
@ -114,13 +120,19 @@ spec_Analyzer = do
|
|||||||
let expectedPages =
|
let expectedPages =
|
||||||
[ ( "HomePage",
|
[ ( "HomePage",
|
||||||
Page.Page
|
Page.Page
|
||||||
{ Page.component = ExtImport (ExtImportModule "Home") "@ext/pages/Main",
|
{ Page.component =
|
||||||
|
ExtImport
|
||||||
|
(ExtImportModule "Home")
|
||||||
|
(fromJust $ SP.parseRelFileP "pages/Main"),
|
||||||
Page.authRequired = Nothing
|
Page.authRequired = Nothing
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
( "ProfilePage",
|
( "ProfilePage",
|
||||||
Page.Page
|
Page.Page
|
||||||
{ Page.component = ExtImport (ExtImportField "profilePage") "@ext/pages/Profile",
|
{ Page.component =
|
||||||
|
ExtImport
|
||||||
|
(ExtImportField "profilePage")
|
||||||
|
(fromJust $ SP.parseRelFileP "pages/Profile"),
|
||||||
Page.authRequired = Just True
|
Page.authRequired = Just True
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -153,7 +165,10 @@ spec_Analyzer = do
|
|||||||
let expectedQueries =
|
let expectedQueries =
|
||||||
[ ( "getUsers",
|
[ ( "getUsers",
|
||||||
Query.Query
|
Query.Query
|
||||||
{ Query.fn = ExtImport (ExtImportField "getAllUsers") "@ext/foo.js",
|
{ Query.fn =
|
||||||
|
ExtImport
|
||||||
|
(ExtImportField "getAllUsers")
|
||||||
|
(fromJust $ SP.parseRelFileP "foo.js"),
|
||||||
Query.entities = Just [Ref "User"],
|
Query.entities = Just [Ref "User"],
|
||||||
Query.auth = Nothing
|
Query.auth = Nothing
|
||||||
}
|
}
|
||||||
@ -164,7 +179,10 @@ spec_Analyzer = do
|
|||||||
let expectedAction =
|
let expectedAction =
|
||||||
[ ( "updateUser",
|
[ ( "updateUser",
|
||||||
Action.Action
|
Action.Action
|
||||||
{ Action.fn = ExtImport (ExtImportField "updateUser") "@ext/foo.js",
|
{ Action.fn =
|
||||||
|
ExtImport
|
||||||
|
(ExtImportField "updateUser")
|
||||||
|
(fromJust $ SP.parseRelFileP "foo.js"),
|
||||||
Action.entities = Just [Ref "User"],
|
Action.entities = Just [Ref "User"],
|
||||||
Action.auth = Just True
|
Action.auth = Just True
|
||||||
}
|
}
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user