gorsee 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +132 -4
- package/package.json +4 -2
- package/src/auth/index.ts +48 -17
- package/src/auth/redis-session-store.ts +46 -0
- package/src/auth/sqlite-session-store.ts +98 -0
- package/src/auth/store-utils.ts +21 -0
- package/src/build/client.ts +25 -7
- package/src/build/manifest.ts +34 -0
- package/src/build/route-metadata.ts +12 -0
- package/src/build/ssg.ts +19 -49
- package/src/cli/bun-plugin.ts +23 -2
- package/src/cli/cmd-build.ts +42 -71
- package/src/cli/cmd-check.ts +40 -26
- package/src/cli/cmd-create.ts +20 -5
- package/src/cli/cmd-deploy.ts +10 -2
- package/src/cli/cmd-dev.ts +9 -9
- package/src/cli/cmd-docs.ts +152 -0
- package/src/cli/cmd-generate.ts +15 -7
- package/src/cli/cmd-migrate.ts +15 -7
- package/src/cli/cmd-routes.ts +12 -5
- package/src/cli/cmd-start.ts +14 -5
- package/src/cli/cmd-test.ts +129 -0
- package/src/cli/cmd-typegen.ts +13 -3
- package/src/cli/cmd-upgrade.ts +143 -0
- package/src/cli/context.ts +12 -0
- package/src/cli/framework-md.ts +43 -16
- package/src/cli/index.ts +18 -0
- package/src/client.ts +26 -0
- package/src/dev/partial-handler.ts +17 -74
- package/src/dev/request-handler.ts +36 -67
- package/src/dev.ts +92 -157
- package/src/index-client.ts +4 -0
- package/src/index.ts +17 -2
- package/src/prod.ts +195 -253
- package/src/runtime/project.ts +73 -0
- package/src/server/cache-utils.ts +23 -0
- package/src/server/cache.ts +37 -14
- package/src/server/html-shell.ts +69 -0
- package/src/server/index.ts +40 -2
- package/src/server/manifest.ts +36 -0
- package/src/server/middleware.ts +18 -2
- package/src/server/not-found.ts +35 -0
- package/src/server/page-render.ts +123 -0
- package/src/server/redis-cache-store.ts +87 -0
- package/src/server/redis-client.ts +71 -0
- package/src/server/request-preflight.ts +45 -0
- package/src/server/route-request.ts +72 -0
- package/src/server/rpc-utils.ts +27 -0
- package/src/server/rpc.ts +70 -18
- package/src/server/sqlite-cache-store.ts +109 -0
- package/src/server/static-file.ts +63 -0
- package/src/server-entry.ts +36 -0
package/src/build/ssg.ts
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
// Routes with `export const prerender = true` are rendered to static HTML
|
|
3
3
|
|
|
4
4
|
import { createRouter } from "../router/scanner.ts"
|
|
5
|
-
import { renderToString, ssrJsx } from "../runtime/server.ts"
|
|
6
5
|
import { createContext } from "../server/middleware.ts"
|
|
7
|
-
import {
|
|
6
|
+
import { renderPageDocument, resolvePageRoute } from "../server/page-render.ts"
|
|
7
|
+
import { wrapHTML, type HTMLWrapOptions } from "../server/html-shell.ts"
|
|
8
8
|
import { join } from "node:path"
|
|
9
9
|
import { mkdir, writeFile } from "node:fs/promises"
|
|
10
10
|
|
|
@@ -16,7 +16,7 @@ export interface SSGResult {
|
|
|
16
16
|
interface SSGOptions {
|
|
17
17
|
routesDir: string
|
|
18
18
|
outDir: string
|
|
19
|
-
wrapHTML
|
|
19
|
+
wrapHTML?: (body: string, options?: HTMLWrapOptions) => string
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
export async function generateStaticPages(options: SSGOptions): Promise<SSGResult> {
|
|
@@ -34,54 +34,20 @@ export async function generateStaticPages(options: SSGOptions): Promise<SSGResul
|
|
|
34
34
|
// Skip routes that don't opt-in to prerendering
|
|
35
35
|
if (!mod.prerender) continue
|
|
36
36
|
|
|
37
|
-
const component = mod.default as Function | undefined
|
|
38
|
-
if (typeof component !== "function") continue
|
|
39
|
-
|
|
40
|
-
// Parallel loading: import layout modules + run page loader
|
|
41
37
|
const fakeRequest = new Request(`http://localhost${route.path}`)
|
|
42
38
|
const ctx = createContext(fakeRequest, {})
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
)
|
|
56
|
-
const layoutLoaderResults = await Promise.all(layoutLoaderPromises)
|
|
57
|
-
|
|
58
|
-
// Nested layout wrapping: outermost first, innermost wraps page
|
|
59
|
-
let pageComponent: Function = component
|
|
60
|
-
for (let i = layoutMods.length - 1; i >= 0; i--) {
|
|
61
|
-
const Layout = layoutMods[i]!.default
|
|
62
|
-
if (typeof Layout === "function") {
|
|
63
|
-
const inner = pageComponent
|
|
64
|
-
const layoutData = layoutLoaderResults[i]
|
|
65
|
-
pageComponent = (props: Record<string, unknown>) =>
|
|
66
|
-
Layout({ ...props, data: layoutData, children: inner(props) })
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// Render
|
|
71
|
-
resetServerHead()
|
|
72
|
-
const pageProps = { params: {}, ctx, data: loaderData }
|
|
73
|
-
const vnode = ssrJsx(pageComponent as any, pageProps)
|
|
74
|
-
const body = renderToString(vnode)
|
|
75
|
-
const headElements = getServerHead()
|
|
76
|
-
|
|
77
|
-
// Extract title
|
|
78
|
-
let title: string | undefined
|
|
79
|
-
for (const el of headElements) {
|
|
80
|
-
const m = el.match(/<title>(.+?)<\/title>/)
|
|
81
|
-
if (m) { title = m[1]; break }
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const html = wrapHTML(body, { title, loaderData, headElements })
|
|
39
|
+
const match = { route, params: {} }
|
|
40
|
+
const resolved = await resolvePageRoute(mod, match, ctx)
|
|
41
|
+
if (!resolved) continue
|
|
42
|
+
|
|
43
|
+
const rendered = renderPageDocument(resolved.pageComponent, ctx, {}, resolved.loaderData)
|
|
44
|
+
const renderHTML = wrapHTML ?? defaultWrapHTML
|
|
45
|
+
const html = renderHTML(rendered.html, {
|
|
46
|
+
title: rendered.title,
|
|
47
|
+
loaderData: resolved.loaderData,
|
|
48
|
+
headElements: rendered.headElements,
|
|
49
|
+
cssFiles: resolved.cssFiles,
|
|
50
|
+
})
|
|
85
51
|
|
|
86
52
|
// Write file
|
|
87
53
|
const outPath = route.path === "/"
|
|
@@ -98,3 +64,7 @@ export async function generateStaticPages(options: SSGOptions): Promise<SSGResul
|
|
|
98
64
|
|
|
99
65
|
return { pages, errors }
|
|
100
66
|
}
|
|
67
|
+
|
|
68
|
+
function defaultWrapHTML(body: string, options: HTMLWrapOptions = {}): string {
|
|
69
|
+
return wrapHTML(body, undefined, options)
|
|
70
|
+
}
|
package/src/cli/bun-plugin.ts
CHANGED
|
@@ -9,8 +9,10 @@ const FRAMEWORK_ROOT = resolve(import.meta.dir, "..")
|
|
|
9
9
|
|
|
10
10
|
const EXPORT_MAP: Record<string, string> = {
|
|
11
11
|
"gorsee": resolve(FRAMEWORK_ROOT, "index.ts"),
|
|
12
|
+
"gorsee/compat": resolve(FRAMEWORK_ROOT, "index.ts"),
|
|
13
|
+
"gorsee/client": resolve(FRAMEWORK_ROOT, "client.ts"),
|
|
12
14
|
"gorsee/reactive": resolve(FRAMEWORK_ROOT, "reactive/index.ts"),
|
|
13
|
-
"gorsee/server": resolve(FRAMEWORK_ROOT, "server
|
|
15
|
+
"gorsee/server": resolve(FRAMEWORK_ROOT, "server-entry.ts"),
|
|
14
16
|
"gorsee/types": resolve(FRAMEWORK_ROOT, "types/index.ts"),
|
|
15
17
|
"gorsee/db": resolve(FRAMEWORK_ROOT, "db/index.ts"),
|
|
16
18
|
"gorsee/router": resolve(FRAMEWORK_ROOT, "router/index.ts"),
|
|
@@ -20,6 +22,25 @@ const EXPORT_MAP: Record<string, string> = {
|
|
|
20
22
|
"gorsee/security": resolve(FRAMEWORK_ROOT, "security/index.ts"),
|
|
21
23
|
"gorsee/jsx-runtime": resolve(FRAMEWORK_ROOT, "jsx-runtime.ts"),
|
|
22
24
|
"gorsee/jsx-dev-runtime": resolve(FRAMEWORK_ROOT, "jsx-runtime.ts"),
|
|
25
|
+
"gorsee/testing": resolve(FRAMEWORK_ROOT, "testing/index.ts"),
|
|
26
|
+
"gorsee/i18n": resolve(FRAMEWORK_ROOT, "i18n/index.ts"),
|
|
27
|
+
"gorsee/env": resolve(FRAMEWORK_ROOT, "env/index.ts"),
|
|
28
|
+
"gorsee/auth": resolve(FRAMEWORK_ROOT, "auth/index.ts"),
|
|
29
|
+
"gorsee/routes": resolve(FRAMEWORK_ROOT, "runtime/typed-routes.ts"),
|
|
30
|
+
"gorsee/cli/cmd-create": resolve(FRAMEWORK_ROOT, "cli/cmd-create.ts"),
|
|
31
|
+
"gorsee/plugins": resolve(FRAMEWORK_ROOT, "plugins/index.ts"),
|
|
32
|
+
"gorsee/plugins/drizzle": resolve(FRAMEWORK_ROOT, "plugins/drizzle.ts"),
|
|
33
|
+
"gorsee/plugins/prisma": resolve(FRAMEWORK_ROOT, "plugins/prisma.ts"),
|
|
34
|
+
"gorsee/plugins/tailwind": resolve(FRAMEWORK_ROOT, "plugins/tailwind.ts"),
|
|
35
|
+
"gorsee/plugins/lucia": resolve(FRAMEWORK_ROOT, "plugins/lucia.ts"),
|
|
36
|
+
"gorsee/plugins/s3": resolve(FRAMEWORK_ROOT, "plugins/s3.ts"),
|
|
37
|
+
"gorsee/plugins/resend": resolve(FRAMEWORK_ROOT, "plugins/resend.ts"),
|
|
38
|
+
"gorsee/plugins/stripe": resolve(FRAMEWORK_ROOT, "plugins/stripe.ts"),
|
|
39
|
+
"gorsee/deploy": resolve(FRAMEWORK_ROOT, "deploy/index.ts"),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveFrameworkImport(specifier: string): string | undefined {
|
|
43
|
+
return EXPORT_MAP[specifier]
|
|
23
44
|
}
|
|
24
45
|
|
|
25
46
|
plugin({
|
|
@@ -27,7 +48,7 @@ plugin({
|
|
|
27
48
|
setup(build) {
|
|
28
49
|
// Resolve gorsee/* imports
|
|
29
50
|
build.onResolve({ filter: /^gorsee(\/.*)?$/ }, (args) => {
|
|
30
|
-
const mapped =
|
|
51
|
+
const mapped = resolveFrameworkImport(args.path)
|
|
31
52
|
if (mapped) {
|
|
32
53
|
return { path: mapped }
|
|
33
54
|
}
|
package/src/cli/cmd-build.ts
CHANGED
|
@@ -6,14 +6,10 @@ import { mkdir, rm, writeFile, readdir, stat, watch } from "node:fs/promises"
|
|
|
6
6
|
import { createRouter } from "../router/scanner.ts"
|
|
7
7
|
import { buildClientBundles } from "../build/client.ts"
|
|
8
8
|
import { generateStaticPages } from "../build/ssg.ts"
|
|
9
|
+
import { createBuildManifest } from "../build/manifest.ts"
|
|
9
10
|
import { createHash } from "node:crypto"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
routes: Record<string, { js?: string; hasLoader: boolean; prerendered?: boolean }>
|
|
13
|
-
chunks: string[]
|
|
14
|
-
prerendered: string[]
|
|
15
|
-
buildTime: string
|
|
16
|
-
}
|
|
11
|
+
import { wrapHTML } from "../server/html-shell.ts"
|
|
12
|
+
import { createProjectContext, type RuntimeOptions } from "../runtime/project.ts"
|
|
17
13
|
|
|
18
14
|
async function hashFile(path: string): Promise<string> {
|
|
19
15
|
const content = await Bun.file(path).arrayBuffer()
|
|
@@ -36,30 +32,33 @@ async function getAllFiles(dir: string): Promise<string[]> {
|
|
|
36
32
|
return files
|
|
37
33
|
}
|
|
38
34
|
|
|
39
|
-
export
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
35
|
+
export interface BuildCommandOptions extends RuntimeOptions {
|
|
36
|
+
minify?: boolean
|
|
37
|
+
sourcemap?: boolean
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function buildProject(options: BuildCommandOptions = {}) {
|
|
41
|
+
const { cwd, paths } = createProjectContext(options)
|
|
44
42
|
const startTime = performance.now()
|
|
45
43
|
console.log("\n Gorsee Build\n")
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
await
|
|
49
|
-
await mkdir(
|
|
50
|
-
await mkdir(join(distDir, "server"), { recursive: true })
|
|
45
|
+
await rm(paths.distDir, { recursive: true, force: true })
|
|
46
|
+
await mkdir(paths.clientDir, { recursive: true })
|
|
47
|
+
await mkdir(paths.serverDir, { recursive: true })
|
|
51
48
|
|
|
52
49
|
// 1. Scan routes
|
|
53
|
-
const
|
|
54
|
-
const routes = await createRouter(routesDir)
|
|
50
|
+
const routes = await createRouter(paths.routesDir)
|
|
55
51
|
console.log(` [1/5] Found ${routes.length} route(s)`)
|
|
56
52
|
|
|
57
53
|
// 2. Build client bundles (minified)
|
|
58
|
-
const build = await buildClientBundles(routes, cwd, {
|
|
54
|
+
const build = await buildClientBundles(routes, cwd, {
|
|
55
|
+
minify: options.minify ?? true,
|
|
56
|
+
sourcemap: options.sourcemap ?? true,
|
|
57
|
+
})
|
|
59
58
|
console.log(` [2/5] Client bundles built (${build.entryMap.size} entries)`)
|
|
60
59
|
|
|
61
60
|
// 3. Hash and copy client files to dist
|
|
62
|
-
const clientSrc = join(
|
|
61
|
+
const clientSrc = join(paths.gorseeDir, "client")
|
|
63
62
|
const clientFiles = await getAllFiles(clientSrc)
|
|
64
63
|
const hashMap = new Map<string, string>()
|
|
65
64
|
|
|
@@ -68,7 +67,7 @@ export async function runBuild(_args: string[]) {
|
|
|
68
67
|
const hash = await hashFile(file)
|
|
69
68
|
const ext = rel.lastIndexOf(".")
|
|
70
69
|
const hashed = ext > 0 ? `${rel.slice(0, ext)}.${hash}${rel.slice(ext)}` : `${rel}.${hash}`
|
|
71
|
-
const dest = join(
|
|
70
|
+
const dest = join(paths.clientDir, hashed)
|
|
72
71
|
await mkdir(join(dest, ".."), { recursive: true })
|
|
73
72
|
await Bun.write(dest, Bun.file(file))
|
|
74
73
|
hashMap.set(rel, hashed)
|
|
@@ -76,56 +75,22 @@ export async function runBuild(_args: string[]) {
|
|
|
76
75
|
console.log(` [3/5] Assets hashed (${hashMap.size} files)`)
|
|
77
76
|
|
|
78
77
|
// 4. Generate manifest
|
|
79
|
-
const manifest
|
|
80
|
-
routes: {},
|
|
81
|
-
chunks: [],
|
|
82
|
-
prerendered: [],
|
|
83
|
-
buildTime: new Date().toISOString(),
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const route of routes) {
|
|
87
|
-
const jsRel = build.entryMap.get(route.path)
|
|
88
|
-
manifest.routes[route.path] = {
|
|
89
|
-
js: jsRel ? hashMap.get(jsRel) : undefined,
|
|
90
|
-
hasLoader: false, // will be detected at runtime
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
for (const [, hashed] of hashMap) {
|
|
95
|
-
if (hashed.includes("chunk-")) manifest.chunks.push(hashed)
|
|
96
|
-
}
|
|
78
|
+
const manifest = await createBuildManifest(routes, build.entryMap, hashMap)
|
|
97
79
|
|
|
98
|
-
await writeFile(join(distDir, "manifest.json"), JSON.stringify(manifest, null, 2))
|
|
80
|
+
await writeFile(join(paths.distDir, "manifest.json"), JSON.stringify(manifest, null, 2))
|
|
99
81
|
console.log(` [4/5] Manifest generated`)
|
|
100
82
|
|
|
101
83
|
// 5. Static Site Generation (prerender pages with `export const prerender = true`)
|
|
102
84
|
const ssgResult = await generateStaticPages({
|
|
103
|
-
routesDir,
|
|
104
|
-
outDir: join(distDir, "static"),
|
|
105
|
-
wrapHTML: (body,
|
|
106
|
-
const title = (opts.title as string) ?? "Gorsee App"
|
|
107
|
-
const headElements = (opts.headElements as string[]) ?? []
|
|
108
|
-
return `<!DOCTYPE html>
|
|
109
|
-
<html lang="en">
|
|
110
|
-
<head>
|
|
111
|
-
<meta charset="UTF-8" />
|
|
112
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
113
|
-
<title>${title}</title>
|
|
114
|
-
<link rel="stylesheet" href="/styles.css" />
|
|
115
|
-
${headElements.join("\n")}
|
|
116
|
-
</head>
|
|
117
|
-
<body>
|
|
118
|
-
<div id="app">${body}</div>
|
|
119
|
-
</body>
|
|
120
|
-
</html>`
|
|
121
|
-
},
|
|
85
|
+
routesDir: paths.routesDir,
|
|
86
|
+
outDir: join(paths.distDir, "static"),
|
|
87
|
+
wrapHTML: (body, options = {}) => wrapHTML(body, undefined, options),
|
|
122
88
|
})
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
89
|
+
const finalManifest = ssgResult.pages.size > 0
|
|
90
|
+
? await createBuildManifest(routes, build.entryMap, hashMap, ssgResult.pages.keys())
|
|
91
|
+
: manifest
|
|
127
92
|
if (ssgResult.pages.size > 0) {
|
|
128
|
-
await writeFile(join(distDir, "manifest.json"), JSON.stringify(
|
|
93
|
+
await writeFile(join(paths.distDir, "manifest.json"), JSON.stringify(finalManifest, null, 2))
|
|
129
94
|
}
|
|
130
95
|
if (ssgResult.errors.length > 0) {
|
|
131
96
|
for (const err of ssgResult.errors) console.error(` SSG error: ${err}`)
|
|
@@ -134,7 +99,7 @@ ${headElements.join("\n")}
|
|
|
134
99
|
|
|
135
100
|
// Stats
|
|
136
101
|
let totalSize = 0
|
|
137
|
-
for (const file of await getAllFiles(
|
|
102
|
+
for (const file of await getAllFiles(paths.clientDir)) {
|
|
138
103
|
const s = await stat(file)
|
|
139
104
|
totalSize += s.size
|
|
140
105
|
}
|
|
@@ -147,13 +112,12 @@ ${headElements.join("\n")}
|
|
|
147
112
|
console.log()
|
|
148
113
|
}
|
|
149
114
|
|
|
150
|
-
async function
|
|
151
|
-
const
|
|
152
|
-
const routesDir = join(cwd, "routes")
|
|
115
|
+
export async function watchBuildProject(options: BuildCommandOptions = {}) {
|
|
116
|
+
const { paths } = createProjectContext(options)
|
|
153
117
|
|
|
154
118
|
console.log("\n Gorsee Build --watch\n")
|
|
155
119
|
console.log(" Performing initial build...")
|
|
156
|
-
await
|
|
120
|
+
await buildProject(options)
|
|
157
121
|
|
|
158
122
|
let building = false
|
|
159
123
|
let queued = false
|
|
@@ -163,7 +127,7 @@ async function runBuildWatch() {
|
|
|
163
127
|
building = true
|
|
164
128
|
try {
|
|
165
129
|
const start = performance.now()
|
|
166
|
-
await
|
|
130
|
+
await buildProject(options)
|
|
167
131
|
const ms = (performance.now() - start).toFixed(0)
|
|
168
132
|
console.log(` Rebuilt in ${ms}ms`)
|
|
169
133
|
} catch (err) {
|
|
@@ -175,8 +139,15 @@ async function runBuildWatch() {
|
|
|
175
139
|
}
|
|
176
140
|
|
|
177
141
|
console.log(" Watching for changes...")
|
|
178
|
-
const watcher = watch(routesDir, { recursive: true })
|
|
142
|
+
const watcher = watch(paths.routesDir, { recursive: true })
|
|
179
143
|
for await (const _event of watcher) {
|
|
180
144
|
await rebuild()
|
|
181
145
|
}
|
|
182
146
|
}
|
|
147
|
+
|
|
148
|
+
export async function runBuild(args: string[], options: BuildCommandOptions = {}) {
|
|
149
|
+
if (args.includes("--watch")) {
|
|
150
|
+
return watchBuildProject(options)
|
|
151
|
+
}
|
|
152
|
+
return buildProject(options)
|
|
153
|
+
}
|
package/src/cli/cmd-check.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { join, relative } from "node:path"
|
|
4
4
|
import { readdir, stat, readFile } from "node:fs/promises"
|
|
5
5
|
import { createRouter } from "../router/scanner.ts"
|
|
6
|
+
import { createProjectContext, type RuntimeOptions } from "../runtime/project.ts"
|
|
6
7
|
|
|
7
8
|
interface CheckResult {
|
|
8
9
|
errors: CheckIssue[]
|
|
@@ -20,6 +21,8 @@ interface CheckIssue {
|
|
|
20
21
|
|
|
21
22
|
const MAX_FILE_LINES = 500
|
|
22
23
|
|
|
24
|
+
export type { CheckResult, CheckIssue }
|
|
25
|
+
|
|
23
26
|
async function getAllTsFiles(dir: string): Promise<string[]> {
|
|
24
27
|
const files: string[] = []
|
|
25
28
|
let entries: string[]
|
|
@@ -144,31 +147,31 @@ async function checkProjectStructure(cwd: string): Promise<CheckIssue[]> {
|
|
|
144
147
|
return issues
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
export
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
export interface CheckCommandOptions extends RuntimeOptions {
|
|
151
|
+
runTypeScript?: boolean
|
|
152
|
+
}
|
|
150
153
|
|
|
154
|
+
export async function checkProject(options: CheckCommandOptions = {}): Promise<CheckResult> {
|
|
155
|
+
const { cwd, paths } = createProjectContext(options)
|
|
156
|
+
const runTypeScript = options.runTypeScript ?? true
|
|
151
157
|
const result: CheckResult = { errors: [], warnings: [], info: [] }
|
|
152
158
|
|
|
153
|
-
// 1. Project structure
|
|
154
159
|
const structIssues = await checkProjectStructure(cwd)
|
|
155
160
|
for (const issue of structIssues) {
|
|
156
161
|
if (issue.code.startsWith("E")) result.errors.push(issue)
|
|
157
162
|
else result.warnings.push(issue)
|
|
158
163
|
}
|
|
159
164
|
|
|
160
|
-
// 2. Routes
|
|
161
165
|
try {
|
|
162
|
-
const routes = await createRouter(
|
|
166
|
+
const routes = await createRouter(paths.routesDir)
|
|
163
167
|
result.info.push(`Found ${routes.length} route(s)`)
|
|
164
168
|
} catch {
|
|
165
169
|
result.info.push("Could not scan routes")
|
|
166
170
|
}
|
|
167
171
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
files.push(...(await getAllTsFiles(
|
|
171
|
-
files.push(...(await getAllTsFiles(join(cwd, "middleware"))))
|
|
172
|
+
const files = await getAllTsFiles(paths.routesDir)
|
|
173
|
+
files.push(...(await getAllTsFiles(paths.sharedDir)))
|
|
174
|
+
files.push(...(await getAllTsFiles(paths.middlewareDir)))
|
|
172
175
|
|
|
173
176
|
for (const file of files) {
|
|
174
177
|
const sizeIssues = await checkFileSize(file, cwd)
|
|
@@ -179,24 +182,35 @@ export async function runCheck(_args: string[]) {
|
|
|
179
182
|
}
|
|
180
183
|
}
|
|
181
184
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
stderr: "pipe",
|
|
188
|
-
})
|
|
189
|
-
const tscExit = await tsc.exited
|
|
190
|
-
if (tscExit !== 0) {
|
|
191
|
-
const stderr = await new Response(tsc.stderr).text()
|
|
192
|
-
result.errors.push({
|
|
193
|
-
code: "TSC",
|
|
194
|
-
file: ".",
|
|
195
|
-
message: `TypeScript errors:\n${stderr.trim()}`,
|
|
185
|
+
if (runTypeScript) {
|
|
186
|
+
const tsc = Bun.spawn(["bun", "x", "tsc", "--noEmit"], {
|
|
187
|
+
cwd,
|
|
188
|
+
stdout: "pipe",
|
|
189
|
+
stderr: "pipe",
|
|
196
190
|
})
|
|
197
|
-
|
|
198
|
-
|
|
191
|
+
const tscExit = await tsc.exited
|
|
192
|
+
if (tscExit !== 0) {
|
|
193
|
+
const stderr = await new Response(tsc.stderr).text()
|
|
194
|
+
result.errors.push({
|
|
195
|
+
code: "TSC",
|
|
196
|
+
file: ".",
|
|
197
|
+
message: `TypeScript errors:\n${stderr.trim()}`,
|
|
198
|
+
})
|
|
199
|
+
} else {
|
|
200
|
+
result.info.push("TypeScript: no errors")
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return result
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/** @deprecated Use checkProject() for programmatic access. */
|
|
208
|
+
export async function runCheck(_args: string[], options: CheckCommandOptions = {}) {
|
|
209
|
+
console.log("\n Gorsee Check\n")
|
|
210
|
+
if (options.runTypeScript ?? true) {
|
|
211
|
+
console.log(" Running TypeScript check...")
|
|
199
212
|
}
|
|
213
|
+
const result = await checkProject(options)
|
|
200
214
|
|
|
201
215
|
// Report
|
|
202
216
|
for (const info of result.info) {
|
package/src/cli/cmd-create.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import { mkdir, writeFile } from "node:fs/promises"
|
|
4
4
|
import { join } from "node:path"
|
|
5
5
|
import { generateFrameworkMD } from "./framework-md.ts"
|
|
6
|
+
import { createProjectContext, type RuntimeOptions } from "../runtime/project.ts"
|
|
6
7
|
|
|
7
8
|
const DIRS = [
|
|
8
9
|
"routes",
|
|
@@ -13,7 +14,7 @@ const DIRS = [
|
|
|
13
14
|
"public",
|
|
14
15
|
]
|
|
15
16
|
|
|
16
|
-
const INDEX_ROUTE = `import { createSignal, Head, Link } from "gorsee"
|
|
17
|
+
const INDEX_ROUTE = `import { createSignal, Head, Link } from "gorsee/client"
|
|
17
18
|
|
|
18
19
|
export default function HomePage() {
|
|
19
20
|
const [count, setCount] = createSignal(0)
|
|
@@ -37,7 +38,7 @@ export default function HomePage() {
|
|
|
37
38
|
}
|
|
38
39
|
`
|
|
39
40
|
|
|
40
|
-
const ABOUT_ROUTE = `import { Head, Link } from "gorsee"
|
|
41
|
+
const ABOUT_ROUTE = `import { Head, Link } from "gorsee/client"
|
|
41
42
|
|
|
42
43
|
export default function AboutPage() {
|
|
43
44
|
return (
|
|
@@ -81,7 +82,7 @@ const ERROR_PAGE = `export default function ErrorPage(props: { error: Error }) {
|
|
|
81
82
|
}
|
|
82
83
|
`
|
|
83
84
|
|
|
84
|
-
const NOT_FOUND_PAGE = `import { Head, Link } from "gorsee"
|
|
85
|
+
const NOT_FOUND_PAGE = `import { Head, Link } from "gorsee/client"
|
|
85
86
|
|
|
86
87
|
export default function NotFoundPage() {
|
|
87
88
|
return (
|
|
@@ -211,6 +212,12 @@ Open [http://localhost:3000](http://localhost:3000).
|
|
|
211
212
|
| \`bunx gorsee typegen\` | Generate typed routes |
|
|
212
213
|
| \`bunx gorsee migrate\` | Run DB migrations |
|
|
213
214
|
|
|
215
|
+
## Import Boundaries
|
|
216
|
+
|
|
217
|
+
- Use \`gorsee/client\` for route components, islands, links, forms, and reactive primitives.
|
|
218
|
+
- Use \`gorsee/server\` for loaders, middleware, auth, db, cache, RPC, security, env, and logging.
|
|
219
|
+
- Do not use root \`gorsee\` in new code. It exists only as a compatibility entrypoint.
|
|
220
|
+
|
|
214
221
|
## Project Structure
|
|
215
222
|
|
|
216
223
|
\`\`\`
|
|
@@ -233,14 +240,17 @@ See \`FRAMEWORK.md\` for the full API reference (AI-friendly).
|
|
|
233
240
|
`
|
|
234
241
|
}
|
|
235
242
|
|
|
236
|
-
export
|
|
243
|
+
export interface CreateCommandOptions extends RuntimeOptions {}
|
|
244
|
+
|
|
245
|
+
export async function createProject(args: string[], options: CreateCommandOptions = {}) {
|
|
237
246
|
const name = args[0]
|
|
238
247
|
if (!name) {
|
|
239
248
|
console.error("Usage: gorsee create <project-name>")
|
|
240
249
|
process.exit(1)
|
|
241
250
|
}
|
|
242
251
|
|
|
243
|
-
const
|
|
252
|
+
const { cwd } = createProjectContext(options)
|
|
253
|
+
const dir = join(cwd, name)
|
|
244
254
|
console.log(`\n Creating ${name}...\n`)
|
|
245
255
|
|
|
246
256
|
await mkdir(dir, { recursive: true })
|
|
@@ -311,3 +321,8 @@ export async function runCreate(args: string[]) {
|
|
|
311
321
|
console.log(" Then open http://localhost:3000")
|
|
312
322
|
console.log()
|
|
313
323
|
}
|
|
324
|
+
|
|
325
|
+
/** @deprecated Use createProject() for programmatic access. */
|
|
326
|
+
export async function runCreate(args: string[], options: CreateCommandOptions = {}) {
|
|
327
|
+
return createProject(args, options)
|
|
328
|
+
}
|
package/src/cli/cmd-deploy.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { writeFile, access, mkdir } from "node:fs/promises"
|
|
4
4
|
import { join } from "node:path"
|
|
5
|
+
import { createProjectContext, type RuntimeOptions } from "../runtime/project.ts"
|
|
5
6
|
|
|
6
7
|
type Target = "vercel" | "fly" | "cloudflare" | "netlify" | "docker"
|
|
7
8
|
|
|
@@ -90,8 +91,10 @@ async function deployDocker(cwd: string): Promise<void> {
|
|
|
90
91
|
console.log(" 2. Run: docker run -p 3000:3000 gorsee-app")
|
|
91
92
|
}
|
|
92
93
|
|
|
93
|
-
export
|
|
94
|
-
|
|
94
|
+
export interface DeployCommandOptions extends RuntimeOptions {}
|
|
95
|
+
|
|
96
|
+
export async function generateDeployConfig(args: string[], options: DeployCommandOptions = {}): Promise<void> {
|
|
97
|
+
const { cwd } = createProjectContext(options)
|
|
95
98
|
const initOnly = args.includes("--init")
|
|
96
99
|
const targetArg = args.find((a) => !a.startsWith("-")) as Target | undefined
|
|
97
100
|
|
|
@@ -139,3 +142,8 @@ export async function runDeploy(args: string[]): Promise<void> {
|
|
|
139
142
|
|
|
140
143
|
console.log()
|
|
141
144
|
}
|
|
145
|
+
|
|
146
|
+
/** @deprecated Use generateDeployConfig() for programmatic access. */
|
|
147
|
+
export async function runDeploy(args: string[], options: DeployCommandOptions = {}): Promise<void> {
|
|
148
|
+
return generateDeployConfig(args, options)
|
|
149
|
+
}
|
package/src/cli/cmd-dev.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
// gorsee dev -- start dev server
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { createProjectContext, type RuntimeOptions } from "../runtime/project.ts"
|
|
4
4
|
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
})
|
|
12
|
-
await
|
|
5
|
+
export interface DevCommandOptions extends RuntimeOptions {
|
|
6
|
+
port?: number
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export async function runDev(_args: string[], options: DevCommandOptions = {}) {
|
|
10
|
+
const { cwd } = createProjectContext(options)
|
|
11
|
+
const { startDevServer } = await import("../dev.ts")
|
|
12
|
+
await startDevServer({ cwd, port: options.port })
|
|
13
13
|
}
|