Skip to content

Commit bb2adc9

Browse files
committed
Refactor project shape, add local config and validation
1 parent 9798922 commit bb2adc9

10 files changed

Lines changed: 94 additions & 64 deletions

File tree

core/entrypoint.ts

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,46 @@ import { glob } from "glob"
22
import { createJiti } from "jiti"
33
import { Database } from "monarch-orm"
44
import path from "node:path"
5+
import { localProjectSchema, projectSchema, type Project } from "./models"
56

67
export type DirTree = { [k: string]: string | DirTree }
78

89
export class Entrypoint {
910
private import = createJiti(import.meta.url).import
1011

12+
public async getProject(
13+
projects: Record<string, Project> | undefined,
14+
): Promise<Project | undefined> {
15+
const configPath = path.resolve("monarch.config.json")
16+
const config = await this.import(configPath).catch(() => undefined)
17+
if (config) return localProjectSchema.safeParse(config).data
18+
const project = projects?.[process.cwd()]
19+
if (project) return projectSchema.safeParse(project).data
20+
}
21+
22+
public async getDatabaseExport(relpath: string, identifier: string) {
23+
try {
24+
const exports = await this.import<any>(path.resolve(relpath))
25+
const db = exports[identifier]
26+
if (db instanceof Database) return db
27+
return null
28+
} catch (_error) {
29+
return null
30+
}
31+
}
32+
33+
public async getFileExports(relpath: string) {
34+
try {
35+
const exports = await this.import<any>(path.resolve(relpath))
36+
return Object.entries(exports).map(([name, value]) => ({
37+
name,
38+
isDb: value instanceof Database,
39+
}))
40+
} catch (_error) {
41+
return []
42+
}
43+
}
44+
1145
public async getFiles() {
1246
const paths = await glob("**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}", {
1347
ignore: "{node_modules,tests,dist}/**",
@@ -37,27 +71,4 @@ export class Entrypoint {
3771

3872
return { tree, suggestions }
3973
}
40-
41-
public async getExports(relpath: string) {
42-
try {
43-
const exports = await this.import<any>(path.resolve(relpath))
44-
return Object.entries(exports).map(([name, value]) => ({
45-
name,
46-
isDb: value instanceof Database,
47-
}))
48-
} catch (_error) {
49-
return []
50-
}
51-
}
52-
53-
public async getExport(relpath: string, identifier: string) {
54-
try {
55-
const exports = await this.import<any>(path.resolve(relpath))
56-
const db = exports[identifier]
57-
if (db instanceof Database) return db
58-
return null
59-
} catch (_error) {
60-
return null
61-
}
62-
}
6374
}

core/models.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { z } from "zod"
2+
3+
export interface Project extends z.infer<typeof projectSchema> {
4+
local?: boolean
5+
}
6+
7+
export const projectSchema = z.object({
8+
file: z.string(),
9+
export: z.string(),
10+
dbname: z.string(),
11+
dbs: z.record(z.string()),
12+
})
13+
14+
export const localProjectSchema = z
15+
.object({
16+
file: z.string(),
17+
export: z.string(),
18+
dburl: z.string(),
19+
})
20+
.transform((data) => ({
21+
file: data.file,
22+
export: data.export,
23+
dbname: "Local",
24+
dbs: { Local: data.dburl },
25+
local: true,
26+
}))

core/types.ts

Lines changed: 0 additions & 6 deletions
This file was deleted.

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
"monarch": "./dist/index.js"
88
},
99
"scripts": {
10-
"cli:build": "tsup cli/index.ts --format esm",
11-
"cli:dev": "tsx --watch --clean cli/index.ts",
10+
"cli:build": "tsup cli/index.ts --clean --format esm",
11+
"cli:dev": "tsx cli/index.ts",
1212
"ui:build": "react-router build",
1313
"ui:dev": "react-router dev --port 6543",
1414
"ui:preview": "react-router-serve ./dist/ui/server/index.js",
@@ -97,6 +97,7 @@
9797
"prettier-plugin-tailwindcss": "^0.6.11",
9898
"tailwindcss": "^3.4.17",
9999
"tsup": "^8.4.0",
100+
"tsx": "^4.19.3",
100101
"typescript": "^5.7.3",
101102
"typescript-eslint": "^8.26.0",
102103
"vite": "^6.2.0",

pnpm-lock.yaml

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ui/routes/_index.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,8 @@ import { sessionStore } from "../store.server"
44
import type { Route } from "./+types/_index"
55

66
export async function loader({ request }: Route.LoaderArgs) {
7-
const cwd = process.cwd()
87
const session = await sessionStore.getSession(request.headers.get("Cookie"))
9-
const projects = session.get("projects")
10-
const project = projects ? projects[cwd] : undefined
8+
const project = await context.entrypoint.getProject(session.get("projects"))
119
if (!project) throw redirect("/setup")
1210

1311
const dbs = await context.database.list()

ui/routes/setup._index.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,20 +25,18 @@ import type { Route } from "./+types/setup._index"
2525

2626
const completeSetup = createAction({
2727
schema: z.object({
28+
file: z.string(),
29+
export: z.string(),
2830
dbname: z.string(),
2931
dburl: z.string(),
30-
entrypoint: z.string(),
31-
export: z.string(),
3232
}),
3333
async handler(ctx, { request }) {
3434
const session = await sessionStore.getSession(request.headers.get("Cookie"))
35-
const projects = session.get("projects")
3635
session.set("projects", {
37-
...projects,
3836
[process.cwd()]: {
39-
entrypoint: ctx.input.entrypoint,
37+
file: ctx.input.file,
4038
export: ctx.input.export,
41-
dburl: ctx.input.dburl,
39+
dbname: ctx.input.dbname,
4240
dbs: { [ctx.input.dbname]: ctx.input.dburl },
4341
},
4442
})
@@ -57,8 +55,7 @@ export async function action(args: Route.ActionArgs) {
5755
export async function loader({ request }: Route.LoaderArgs) {
5856
const cwd = process.cwd()
5957
const session = await sessionStore.getSession(request.headers.get("Cookie"))
60-
const projects = session.get("projects")
61-
const project = projects ? projects[cwd] : undefined
58+
const project = await context.entrypoint.getProject(session.get("projects"))
6259
if (project) throw redirect("/")
6360

6461
const files = await context.entrypoint.getFiles()
@@ -106,7 +103,7 @@ function ConnectSchema(props: { tree: DirTree; suggestions: string[] }) {
106103
setDraftProject({
107104
step: 2,
108105
project: {
109-
entrypoint: value.file,
106+
file: value.file,
110107
export: value.identifier,
111108
},
112109
})
@@ -119,7 +116,7 @@ function ConnectSchema(props: { tree: DirTree; suggestions: string[] }) {
119116
return (
120117
<form onSubmit={handleSubmit} className="grid gap-4">
121118
<p className="text-xs">
122-
Select the file containing the monarch database export
119+
Select file containing a monarch database export
123120
</p>
124121

125122
<SchemaSelector
@@ -148,7 +145,7 @@ function ConnectDatabase() {
148145
step: 3,
149146
project: {
150147
...draft.project,
151-
dburl,
148+
dbname,
152149
dbs: { [dbname]: dburl },
153150
},
154151
})
@@ -168,16 +165,20 @@ function ConnectDatabase() {
168165
<input type="hidden" name="_action" value="connectDatabase" />
169166
<div className="grid gap-1">
170167
<label className="text-xs font-medium">Database name</label>
171-
<Input type="text" name="dbname" placeholder="Database name" />
168+
<Input
169+
type="text"
170+
name="dbname"
171+
placeholder="Enter database name"
172+
defaultValue="Default"
173+
/>
172174
</div>
173175
<div className="grid gap-1">
174-
<label className="text-xs font-medium">Connection string</label>
175-
<Input type="url" name="dburl" placeholder="Database URL" />
176+
<label className="text-xs font-medium">Database URL</label>
177+
<Input type="url" name="dburl" placeholder="Enter database URL" />
176178
</div>
177179

178180
<p className="text-xs text-muted-foreground">
179-
You can add multiple database connections (dev and prod) and switch
180-
between them.
181+
You can add multiple database connections and switch between them.
181182
</p>
182183

183184
<div className="mt-4 flex justify-between gap-4">
@@ -199,12 +200,11 @@ function CompleteSetup({
199200
const fetcher = useFetcher<typeof action>()
200201

201202
const submitAction = async () => {
202-
const dbname = Object.keys(draft.project.dbs)[0]!
203203
const data = {
204-
dbname,
205-
dburl: draft.project.dburl,
206-
entrypoint: draft.project.entrypoint,
204+
file: draft.project.file,
207205
export: draft.project.export,
206+
dbname: draft.project.dbname,
207+
dburl: draft.project.dbs[draft.project.dbname]!,
208208
} satisfies InferActionInput<typeof completeSetup>
209209
await fetcher.submit(
210210
{

ui/routes/setup.exports.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ import type { Route } from "./+types/setup.exports"
44
export async function loader({ request }: Route.LoaderArgs) {
55
const path = new URL(request.url).searchParams.get("path")
66
if (!path) return []
7-
const exports = await context.entrypoint.getExports(path)
7+
const exports = await context.entrypoint.getFileExports(path)
88
return exports
99
}

ui/store.server.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { createCookieSessionStorage } from "react-router"
22
import { createThemeSessionResolver } from "remix-themes"
3-
import type { Project } from "~/core/types"
3+
import type { Project } from "~/core/models"
44

55
type Store = {
66
projects: Record<string, Project>

ui/utils/draft-project.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Project } from "~/core/types"
1+
import type { Project } from "~/core/models"
22

33
export type DraftProject =
44
| {
@@ -7,11 +7,11 @@ export type DraftProject =
77
}
88
| {
99
step: 2
10-
project: Pick<Project, "entrypoint" | "export">
10+
project: Pick<Project, "file" | "export">
1111
}
1212
| {
1313
step: 3
14-
project: Project
14+
project: Pick<Project, "file" | "export" | "dbname" | "dbs">
1515
}
1616

1717
export const initialDraftProject: DraftProject = { step: 1, project: null }
@@ -37,7 +37,7 @@ export function getPreviousDraftProject(): DraftProject {
3737
return {
3838
step: 2,
3939
project: {
40-
entrypoint: draft.project.entrypoint,
40+
file: draft.project.file,
4141
export: draft.project.export,
4242
},
4343
}

0 commit comments

Comments
 (0)