@nuraly/lumenjs 0.1.3 → 0.1.4
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 +62 -282
- package/dist/auth/config.d.ts +23 -0
- package/dist/auth/config.js +115 -0
- package/dist/auth/guard.d.ts +12 -0
- package/dist/auth/guard.js +28 -0
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/middleware.d.ts +23 -0
- package/dist/auth/middleware.js +89 -0
- package/dist/auth/native-auth.d.ts +73 -0
- package/dist/auth/native-auth.js +293 -0
- package/dist/auth/oidc-client.d.ts +17 -0
- package/dist/auth/oidc-client.js +123 -0
- package/dist/auth/providers/google.d.ts +23 -0
- package/dist/auth/providers/google.js +25 -0
- package/dist/auth/providers/index.d.ts +2 -0
- package/dist/auth/providers/index.js +1 -0
- package/dist/auth/routes/login.d.ts +8 -0
- package/dist/auth/routes/login.js +98 -0
- package/dist/auth/routes/logout.d.ts +4 -0
- package/dist/auth/routes/logout.js +79 -0
- package/dist/auth/routes/oidc-callback.d.ts +3 -0
- package/dist/auth/routes/oidc-callback.js +70 -0
- package/dist/auth/routes/password.d.ts +5 -0
- package/dist/auth/routes/password.js +149 -0
- package/dist/auth/routes/signup.d.ts +3 -0
- package/dist/auth/routes/signup.js +81 -0
- package/dist/auth/routes/token.d.ts +4 -0
- package/dist/auth/routes/token.js +70 -0
- package/dist/auth/routes/utils.d.ts +7 -0
- package/dist/auth/routes/utils.js +35 -0
- package/dist/auth/routes/verify.d.ts +3 -0
- package/dist/auth/routes/verify.js +26 -0
- package/dist/auth/routes.d.ts +8 -0
- package/dist/auth/routes.js +110 -0
- package/dist/auth/session.d.ts +8 -0
- package/dist/auth/session.js +54 -0
- package/dist/auth/token.d.ts +33 -0
- package/dist/auth/token.js +90 -0
- package/dist/auth/types.d.ts +156 -0
- package/dist/auth/types.js +2 -0
- package/dist/build/build-client.d.ts +15 -0
- package/dist/build/build-client.js +45 -0
- package/dist/build/build-prerender.d.ts +11 -0
- package/dist/build/build-prerender.js +159 -0
- package/dist/build/build-server.d.ts +17 -0
- package/dist/build/build-server.js +98 -0
- package/dist/build/build.js +48 -120
- package/dist/build/scan.d.ts +17 -0
- package/dist/build/scan.js +76 -6
- package/dist/build/serve-api.js +8 -2
- package/dist/build/serve-loaders.d.ts +4 -4
- package/dist/build/serve-loaders.js +26 -18
- package/dist/build/serve-ssr.js +38 -11
- package/dist/build/serve-static.js +3 -3
- package/dist/build/serve.js +218 -15
- package/dist/cli.js +37 -6
- package/dist/communication/encryption.d.ts +35 -0
- package/dist/communication/encryption.js +90 -0
- package/dist/communication/handlers/context.d.ts +27 -0
- package/dist/communication/handlers/context.js +1 -0
- package/dist/communication/handlers/conversation.d.ts +24 -0
- package/dist/communication/handlers/conversation.js +113 -0
- package/dist/communication/handlers/file-upload.d.ts +17 -0
- package/dist/communication/handlers/file-upload.js +62 -0
- package/dist/communication/handlers/messaging.d.ts +30 -0
- package/dist/communication/handlers/messaging.js +237 -0
- package/dist/communication/handlers/presence.d.ts +15 -0
- package/dist/communication/handlers/presence.js +76 -0
- package/dist/communication/handlers.d.ts +5 -0
- package/dist/communication/handlers.js +5 -0
- package/dist/communication/index.d.ts +9 -0
- package/dist/communication/index.js +7 -0
- package/dist/communication/link-preview.d.ts +18 -0
- package/dist/communication/link-preview.js +115 -0
- package/dist/communication/schema.d.ts +10 -0
- package/dist/communication/schema.js +101 -0
- package/dist/communication/server.d.ts +86 -0
- package/dist/communication/server.js +212 -0
- package/dist/communication/signaling.d.ts +43 -0
- package/dist/communication/signaling.js +271 -0
- package/dist/communication/store.d.ts +71 -0
- package/dist/communication/store.js +289 -0
- package/dist/communication/types.d.ts +454 -0
- package/dist/communication/types.js +1 -0
- package/dist/create.d.ts +1 -0
- package/dist/create.js +55 -0
- package/dist/db/auto-migrate.d.ts +3 -0
- package/dist/db/auto-migrate.js +100 -0
- package/dist/db/client.d.ts +3 -0
- package/dist/db/client.js +18 -0
- package/dist/db/index.d.ts +17 -13
- package/dist/db/index.js +205 -26
- package/dist/db/seed.d.ts +12 -0
- package/dist/db/seed.js +88 -0
- package/dist/db/table.d.ts +10 -0
- package/dist/db/table.js +12 -0
- package/dist/dev-server/config.d.ts +11 -0
- package/dist/dev-server/config.js +23 -20
- package/dist/dev-server/index-html.d.ts +3 -0
- package/dist/dev-server/index-html.js +18 -6
- package/dist/dev-server/nuralyui-aliases.d.ts +0 -4
- package/dist/dev-server/nuralyui-aliases.js +115 -94
- package/dist/dev-server/plugins/vite-plugin-api-routes.js +29 -5
- package/dist/dev-server/plugins/vite-plugin-auth.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-auth.js +223 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.d.ts +16 -0
- package/dist/dev-server/plugins/vite-plugin-auto-define.js +111 -0
- package/dist/dev-server/plugins/vite-plugin-communication.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-communication.js +205 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-editor-api.js +318 -0
- package/dist/dev-server/plugins/vite-plugin-i18n.js +69 -2
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +6 -0
- package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +78 -34
- package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +44 -2
- package/dist/dev-server/plugins/vite-plugin-llms.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-llms.js +92 -0
- package/dist/dev-server/plugins/vite-plugin-loaders.js +146 -13
- package/dist/dev-server/plugins/vite-plugin-routes.js +15 -5
- package/dist/dev-server/plugins/vite-plugin-socketio.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-socketio.js +51 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +2 -0
- package/dist/dev-server/plugins/vite-plugin-source-annotator.js +26 -3
- package/dist/dev-server/plugins/vite-plugin-storage.d.ts +10 -0
- package/dist/dev-server/plugins/vite-plugin-storage.js +126 -0
- package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +111 -2
- package/dist/dev-server/server.js +127 -13
- package/dist/dev-server/ssr-render.d.ts +2 -1
- package/dist/dev-server/ssr-render.js +107 -48
- package/dist/editor/ai/backend.d.ts +20 -0
- package/dist/editor/ai/backend.js +104 -0
- package/dist/editor/ai/claude-code-client.d.ts +20 -0
- package/dist/editor/ai/claude-code-client.js +145 -0
- package/dist/editor/ai/opencode-client.d.ts +14 -0
- package/dist/editor/ai/opencode-client.js +125 -0
- package/dist/editor/ai/snapshot-store.d.ts +22 -0
- package/dist/editor/ai/snapshot-store.js +35 -0
- package/dist/editor/ai/types.d.ts +30 -0
- package/dist/editor/ai/types.js +136 -0
- package/dist/editor/ai-chat-panel.d.ts +13 -0
- package/dist/editor/ai-chat-panel.js +587 -0
- package/dist/editor/ai-markdown.d.ts +10 -0
- package/dist/editor/ai-markdown.js +70 -0
- package/dist/editor/ai-project-panel.d.ts +11 -0
- package/dist/editor/ai-project-panel.js +332 -0
- package/dist/editor/ast-modification.d.ts +11 -0
- package/dist/editor/ast-modification.js +1 -0
- package/dist/editor/ast-service.d.ts +30 -0
- package/dist/editor/ast-service.js +180 -0
- package/dist/editor/css-rules.d.ts +54 -0
- package/dist/editor/css-rules.js +423 -0
- package/dist/editor/editor-api-client.d.ts +51 -0
- package/dist/editor/editor-api-client.js +162 -0
- package/dist/editor/editor-bridge.d.ts +1 -0
- package/dist/editor/editor-bridge.js +17 -8
- package/dist/editor/editor-toolbar.d.ts +14 -0
- package/dist/editor/editor-toolbar.js +115 -0
- package/dist/editor/file-editor.d.ts +9 -0
- package/dist/editor/file-editor.js +236 -0
- package/dist/editor/file-service.d.ts +16 -0
- package/dist/editor/file-service.js +52 -0
- package/dist/editor/i18n-key-gen.d.ts +1 -0
- package/dist/editor/i18n-key-gen.js +7 -0
- package/dist/editor/inline-text-edit.d.ts +5 -0
- package/dist/editor/inline-text-edit.js +173 -92
- package/dist/editor/overlay-events.d.ts +5 -0
- package/dist/editor/overlay-events.js +364 -0
- package/dist/editor/overlay-hmr.d.ts +2 -0
- package/dist/editor/overlay-hmr.js +75 -0
- package/dist/editor/overlay-selection.d.ts +29 -0
- package/dist/editor/overlay-selection.js +148 -0
- package/dist/editor/overlay-utils.d.ts +12 -0
- package/dist/editor/overlay-utils.js +59 -0
- package/dist/editor/properties-panel-persist.d.ts +14 -0
- package/dist/editor/properties-panel-persist.js +70 -0
- package/dist/editor/properties-panel-rows.d.ts +10 -0
- package/dist/editor/properties-panel-rows.js +349 -0
- package/dist/editor/properties-panel-styles.d.ts +4 -0
- package/dist/editor/properties-panel-styles.js +174 -0
- package/dist/editor/properties-panel.d.ts +4 -0
- package/dist/editor/properties-panel.js +148 -0
- package/dist/editor/property-registry.d.ts +16 -0
- package/dist/editor/property-registry.js +303 -0
- package/dist/editor/standalone-file-panel.d.ts +0 -0
- package/dist/editor/standalone-file-panel.js +1 -0
- package/dist/editor/standalone-overlay-dom.d.ts +0 -0
- package/dist/editor/standalone-overlay-dom.js +1 -0
- package/dist/editor/standalone-overlay-styles.d.ts +0 -0
- package/dist/editor/standalone-overlay-styles.js +1 -0
- package/dist/editor/standalone-overlay.d.ts +1 -0
- package/dist/editor/standalone-overlay.js +76 -0
- package/dist/editor/syntax-highlighter.d.ts +4 -0
- package/dist/editor/syntax-highlighter.js +81 -0
- package/dist/editor/text-toolbar.d.ts +11 -0
- package/dist/editor/text-toolbar.js +327 -0
- package/dist/editor/toolbar-styles.d.ts +4 -0
- package/dist/editor/toolbar-styles.js +198 -0
- package/dist/email/index.d.ts +32 -0
- package/dist/email/index.js +154 -0
- package/dist/email/providers/resend.d.ts +2 -0
- package/dist/email/providers/resend.js +24 -0
- package/dist/email/providers/sendgrid.d.ts +2 -0
- package/dist/email/providers/sendgrid.js +31 -0
- package/dist/email/providers/smtp.d.ts +13 -0
- package/dist/email/providers/smtp.js +125 -0
- package/dist/email/template-engine.d.ts +18 -0
- package/dist/email/template-engine.js +116 -0
- package/dist/email/templates/base.d.ts +9 -0
- package/dist/email/templates/base.js +65 -0
- package/dist/email/templates/password-reset.d.ts +5 -0
- package/dist/email/templates/password-reset.js +15 -0
- package/dist/email/templates/verify-email.d.ts +5 -0
- package/dist/email/templates/verify-email.js +15 -0
- package/dist/email/templates/welcome.d.ts +5 -0
- package/dist/email/templates/welcome.js +13 -0
- package/dist/email/types.d.ts +49 -0
- package/dist/email/types.js +1 -0
- package/dist/llms/generate.d.ts +46 -0
- package/dist/llms/generate.js +185 -0
- package/dist/permissions/guard.d.ts +28 -0
- package/dist/permissions/guard.js +30 -0
- package/dist/permissions/index.d.ts +6 -0
- package/dist/permissions/index.js +3 -0
- package/dist/permissions/service.d.ts +80 -0
- package/dist/permissions/service.js +210 -0
- package/dist/permissions/tables.d.ts +5 -0
- package/dist/permissions/tables.js +68 -0
- package/dist/permissions/types.d.ts +33 -0
- package/dist/permissions/types.js +1 -0
- package/dist/runtime/app-shell.js +163 -0
- package/dist/runtime/auth.d.ts +10 -0
- package/dist/runtime/auth.js +30 -0
- package/dist/runtime/communication.d.ts +137 -0
- package/dist/runtime/communication.js +228 -0
- package/dist/runtime/error-boundary.d.ts +23 -0
- package/dist/runtime/error-boundary.js +120 -0
- package/dist/runtime/i18n.d.ts +6 -1
- package/dist/runtime/i18n.js +42 -21
- package/dist/runtime/router-data.d.ts +3 -0
- package/dist/runtime/router-data.js +102 -17
- package/dist/runtime/router-hydration.js +25 -0
- package/dist/runtime/router.d.ts +16 -1
- package/dist/runtime/router.js +188 -42
- package/dist/runtime/socket-client.d.ts +2 -0
- package/dist/runtime/socket-client.js +30 -0
- package/dist/runtime/webrtc.d.ts +47 -0
- package/dist/runtime/webrtc.js +178 -0
- package/dist/shared/graceful-shutdown.d.ts +8 -0
- package/dist/shared/graceful-shutdown.js +36 -0
- package/dist/shared/health.d.ts +8 -0
- package/dist/shared/health.js +25 -0
- package/dist/shared/llms-txt.d.ts +31 -0
- package/dist/shared/llms-txt.js +85 -0
- package/dist/shared/logger.d.ts +32 -0
- package/dist/shared/logger.js +93 -0
- package/dist/shared/meta.d.ts +27 -0
- package/dist/shared/meta.js +71 -0
- package/dist/shared/middleware-runner.d.ts +9 -0
- package/dist/shared/middleware-runner.js +29 -0
- package/dist/shared/rate-limit.d.ts +18 -0
- package/dist/shared/rate-limit.js +71 -0
- package/dist/shared/request-id.d.ts +5 -0
- package/dist/shared/request-id.js +18 -0
- package/dist/shared/route-matching.js +16 -1
- package/dist/shared/security-headers.d.ts +18 -0
- package/dist/shared/security-headers.js +38 -0
- package/dist/shared/socket-io-setup.d.ts +11 -0
- package/dist/shared/socket-io-setup.js +51 -0
- package/dist/shared/types.d.ts +14 -0
- package/dist/shared/utils.d.ts +33 -7
- package/dist/shared/utils.js +164 -27
- package/dist/storage/adapters/local.d.ts +44 -0
- package/dist/storage/adapters/local.js +85 -0
- package/dist/storage/adapters/s3.d.ts +32 -0
- package/dist/storage/adapters/s3.js +116 -0
- package/dist/storage/adapters/types.d.ts +53 -0
- package/dist/storage/adapters/types.js +1 -0
- package/dist/storage/index.d.ts +76 -0
- package/dist/storage/index.js +83 -0
- package/package.json +19 -7
- package/templates/blog/api/posts.ts +4 -18
- package/templates/blog/data/migrations/001_init.sql +6 -5
- package/templates/blog/lumenjs.config.ts +3 -0
- package/templates/blog/package.json +14 -0
- package/templates/blog/pages/_layout.ts +25 -0
- package/templates/blog/pages/index.ts +48 -22
- package/templates/blog/pages/posts/[slug].ts +45 -20
- package/templates/blog/pages/tag/[tag].ts +44 -0
- package/templates/dashboard/api/stats.ts +8 -5
- package/templates/dashboard/lumenjs.config.ts +3 -0
- package/templates/dashboard/package.json +14 -0
- package/templates/dashboard/pages/_layout.ts +25 -0
- package/templates/dashboard/pages/index.ts +54 -23
- package/templates/dashboard/pages/settings/index.ts +29 -0
- package/templates/default/lumenjs.config.ts +3 -0
- package/templates/default/package.json +14 -0
- package/templates/default/pages/index.ts +24 -0
- package/templates/social/api/posts/[id].ts +14 -0
- package/templates/social/api/posts.ts +11 -0
- package/templates/social/api/profile/[username].ts +10 -0
- package/templates/social/api/upload.ts +19 -0
- package/templates/social/data/migrations/001_init.sql +78 -0
- package/templates/social/data/migrations/002_add_image_url.sql +1 -0
- package/templates/social/data/migrations/003_auth.sql +7 -0
- package/templates/social/docs/architecture.md +76 -0
- package/templates/social/docs/components.md +100 -0
- package/templates/social/docs/data.md +89 -0
- package/templates/social/docs/pages.md +96 -0
- package/templates/social/docs/theming.md +52 -0
- package/templates/social/lib/media.ts +130 -0
- package/templates/social/lumenjs.auth.ts +21 -0
- package/templates/social/lumenjs.config.ts +3 -0
- package/templates/social/package.json +5 -0
- package/templates/social/pages/_layout.ts +239 -0
- package/templates/social/pages/apps/[id].ts +173 -0
- package/templates/social/pages/apps/index.ts +116 -0
- package/templates/social/pages/auth/login.ts +92 -0
- package/templates/social/pages/bookmarks.ts +57 -0
- package/templates/social/pages/explore.ts +73 -0
- package/templates/social/pages/index.ts +351 -0
- package/templates/social/pages/messages.ts +298 -0
- package/templates/social/pages/new.ts +77 -0
- package/templates/social/pages/notifications.ts +73 -0
- package/templates/social/pages/post/[id].ts +124 -0
- package/templates/social/pages/profile/[username].ts +100 -0
- package/templates/social/pages/settings/accessibility.ts +153 -0
- package/templates/social/pages/settings/account.ts +260 -0
- package/templates/social/pages/settings/help.ts +141 -0
- package/templates/social/pages/settings/language.ts +103 -0
- package/templates/social/pages/settings/privacy.ts +183 -0
- package/templates/social/pages/settings/security.ts +133 -0
- package/templates/social/pages/settings.ts +185 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { pathToFileURL } from 'url';
|
|
4
|
+
import { filePathToTagName, stripOuterLitMarkers, dirToLayoutTagName, isRedirectResponse, patchLoaderDataSpread } from '../shared/utils.js';
|
|
5
|
+
import { installDomShims } from '../shared/dom-shims.js';
|
|
6
|
+
import { getLayoutDirsForPage } from './scan.js';
|
|
7
|
+
export async function prerenderPages(opts) {
|
|
8
|
+
const { serverDir, clientDir, pagesDir, pageEntries, layoutEntries, manifest } = opts;
|
|
9
|
+
const prerenderEntries = pageEntries.filter(e => e.prerender);
|
|
10
|
+
if (prerenderEntries.length === 0)
|
|
11
|
+
return;
|
|
12
|
+
console.log(`[LumenJS] Pre-rendering ${prerenderEntries.length} page(s)...`);
|
|
13
|
+
// Load SSR runtime (installs global DOM shims)
|
|
14
|
+
const ssrRuntimePath = pathToFileURL(path.join(serverDir, 'ssr-runtime.js')).href;
|
|
15
|
+
const ssrRuntime = await import(ssrRuntimePath);
|
|
16
|
+
const { render, html, unsafeStatic } = ssrRuntime;
|
|
17
|
+
// Install additional DOM shims
|
|
18
|
+
installDomShims();
|
|
19
|
+
// Read the built index.html shell
|
|
20
|
+
const indexHtmlShell = fs.readFileSync(path.join(clientDir, 'index.html'), 'utf-8');
|
|
21
|
+
let prerenderCount = 0;
|
|
22
|
+
for (const page of prerenderEntries) {
|
|
23
|
+
// Resolve server module path (Rollup sanitizes brackets in filenames)
|
|
24
|
+
let modulePath = path.join(serverDir, `pages/${page.name}.js`);
|
|
25
|
+
if (!fs.existsSync(modulePath)) {
|
|
26
|
+
modulePath = path.join(serverDir, `pages/${page.name}.js`.replace(/\[/g, '_').replace(/\]/g, '_'));
|
|
27
|
+
}
|
|
28
|
+
if (!fs.existsSync(modulePath)) {
|
|
29
|
+
console.warn(` Skipping ${page.routePath}: server module not found`);
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const mod = await import(pathToFileURL(modulePath).href);
|
|
33
|
+
// Determine paths to pre-render
|
|
34
|
+
const isDynamic = page.routePath.includes(':');
|
|
35
|
+
let pathsToRender = [];
|
|
36
|
+
if (isDynamic) {
|
|
37
|
+
// Dynamic route — call prerenderPaths() for param combinations
|
|
38
|
+
if (typeof mod.prerenderPaths === 'function') {
|
|
39
|
+
const paramsList = await mod.prerenderPaths();
|
|
40
|
+
for (const params of paramsList) {
|
|
41
|
+
// Build pathname from route pattern and params
|
|
42
|
+
let pathname = page.routePath;
|
|
43
|
+
for (const [key, value] of Object.entries(params)) {
|
|
44
|
+
// Handle both :param and :...param (catch-all) patterns
|
|
45
|
+
pathname = pathname.replace(`:...${key}`, value).replace(`:${key}`, value);
|
|
46
|
+
}
|
|
47
|
+
pathsToRender.push({ pathname, params: params });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.warn(` Skipping ${page.routePath}: dynamic route without prerenderPaths()`);
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Static route — render once
|
|
57
|
+
pathsToRender.push({ pathname: page.routePath, params: {} });
|
|
58
|
+
}
|
|
59
|
+
for (const { pathname, params } of pathsToRender) {
|
|
60
|
+
// Run loader if present
|
|
61
|
+
let loaderData = undefined;
|
|
62
|
+
if (mod.loader && typeof mod.loader === 'function') {
|
|
63
|
+
loaderData = await mod.loader({ params, query: {}, url: pathname, headers: {} });
|
|
64
|
+
if (isRedirectResponse(loaderData)) {
|
|
65
|
+
console.warn(` Skipping ${pathname}: loader returned redirect`);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Get tag name
|
|
70
|
+
const relPath = path.relative(pagesDir, page.filePath).replace(/\\/g, '/');
|
|
71
|
+
const tagName = filePathToTagName(relPath);
|
|
72
|
+
// Load and render layout chain
|
|
73
|
+
const routeLayouts = getLayoutDirsForPage(page.filePath, pagesDir, layoutEntries);
|
|
74
|
+
const layoutModules = [];
|
|
75
|
+
const layoutsData = [];
|
|
76
|
+
for (const dir of routeLayouts) {
|
|
77
|
+
const layout = layoutEntries.find(l => l.dir === dir);
|
|
78
|
+
if (!layout)
|
|
79
|
+
continue;
|
|
80
|
+
let layoutLoaderData = undefined;
|
|
81
|
+
if (layout.hasLoader) {
|
|
82
|
+
const manifestLayout = manifest.layouts.find(l => l.dir === dir);
|
|
83
|
+
if (manifestLayout?.module) {
|
|
84
|
+
const layoutModPath = path.join(serverDir, manifestLayout.module);
|
|
85
|
+
if (fs.existsSync(layoutModPath)) {
|
|
86
|
+
const layoutMod = await import(pathToFileURL(layoutModPath).href);
|
|
87
|
+
if (layoutMod.loader && typeof layoutMod.loader === 'function') {
|
|
88
|
+
layoutLoaderData = await layoutMod.loader({ params: {}, query: {}, url: pathname, headers: {} });
|
|
89
|
+
if (isRedirectResponse(layoutLoaderData))
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const layoutTagName = dirToLayoutTagName(dir);
|
|
96
|
+
layoutModules.push({ tagName: layoutTagName, loaderData: layoutLoaderData });
|
|
97
|
+
layoutsData.push({ loaderPath: dir, data: layoutLoaderData });
|
|
98
|
+
}
|
|
99
|
+
// Patch element classes to spread loaderData
|
|
100
|
+
for (const lm of layoutModules) {
|
|
101
|
+
patchLoaderDataSpread(lm.tagName);
|
|
102
|
+
}
|
|
103
|
+
patchLoaderDataSpread(tagName);
|
|
104
|
+
// SSR render page
|
|
105
|
+
const pageTag = unsafeStatic(tagName);
|
|
106
|
+
const pageTemplate = html `<${pageTag} .loaderData=${loaderData}></${pageTag}>`;
|
|
107
|
+
let ssrHtml = '';
|
|
108
|
+
for (const chunk of render(pageTemplate)) {
|
|
109
|
+
ssrHtml += typeof chunk === 'string' ? chunk : String(chunk);
|
|
110
|
+
}
|
|
111
|
+
ssrHtml = stripOuterLitMarkers(ssrHtml);
|
|
112
|
+
// Wrap in layout chain (inside-out, deepest first)
|
|
113
|
+
for (let i = layoutModules.length - 1; i >= 0; i--) {
|
|
114
|
+
const lTag = unsafeStatic(layoutModules[i].tagName);
|
|
115
|
+
const lData = layoutModules[i].loaderData;
|
|
116
|
+
const lTemplate = html `<${lTag} .loaderData=${lData}></${lTag}>`;
|
|
117
|
+
let lHtml = '';
|
|
118
|
+
for (const chunk of render(lTemplate)) {
|
|
119
|
+
lHtml += typeof chunk === 'string' ? chunk : String(chunk);
|
|
120
|
+
}
|
|
121
|
+
if (i > 0) {
|
|
122
|
+
lHtml = stripOuterLitMarkers(lHtml);
|
|
123
|
+
}
|
|
124
|
+
const closingTag = `</${layoutModules[i].tagName}>`;
|
|
125
|
+
const closingIdx = lHtml.lastIndexOf(closingTag);
|
|
126
|
+
if (closingIdx !== -1) {
|
|
127
|
+
ssrHtml = lHtml.slice(0, closingIdx) + ssrHtml + lHtml.slice(closingIdx);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
ssrHtml = lHtml + ssrHtml;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
// Build SSR data script
|
|
134
|
+
const ssrDataObj = layoutsData.length > 0
|
|
135
|
+
? { page: loaderData, layouts: layoutsData }
|
|
136
|
+
: loaderData;
|
|
137
|
+
const loaderDataScript = ssrDataObj !== undefined
|
|
138
|
+
? `<script type="application/json" id="__nk_ssr_data__">${JSON.stringify(ssrDataObj).replace(/</g, '\\u003c')}</script>`
|
|
139
|
+
: '';
|
|
140
|
+
const hydrateScript = `<script type="module">import '@lit-labs/ssr-client/lit-element-hydrate-support.js';</script>`;
|
|
141
|
+
// Build final HTML from the shell
|
|
142
|
+
let htmlOut = indexHtmlShell;
|
|
143
|
+
htmlOut = htmlOut.replace('<script type="module"', `${hydrateScript}\n <script type="module"`);
|
|
144
|
+
htmlOut = htmlOut.replace(/<nk-app><\/nk-app>/, `${loaderDataScript}<nk-app data-nk-ssr><div id="nk-router-outlet">${ssrHtml}</div></nk-app>`);
|
|
145
|
+
// Write pre-rendered HTML file
|
|
146
|
+
const outPath = pathname === '/'
|
|
147
|
+
? path.join(clientDir, 'index.html')
|
|
148
|
+
: path.join(clientDir, pathname, 'index.html');
|
|
149
|
+
// Don't overwrite the root index.html for non-root pages
|
|
150
|
+
if (pathname !== '/') {
|
|
151
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
152
|
+
}
|
|
153
|
+
fs.writeFileSync(outPath, htmlOut);
|
|
154
|
+
prerenderCount++;
|
|
155
|
+
console.log(` Pre-rendered: ${pathname}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
console.log(`[LumenJS] Pre-rendered ${prerenderCount} page(s).`);
|
|
159
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type UserConfig, type Plugin } from 'vite';
|
|
2
|
+
import type { PageEntry, LayoutEntry, ApiEntry } from './scan.js';
|
|
3
|
+
export interface BuildServerOptions {
|
|
4
|
+
projectDir: string;
|
|
5
|
+
serverDir: string;
|
|
6
|
+
pageEntries: PageEntry[];
|
|
7
|
+
layoutEntries: LayoutEntry[];
|
|
8
|
+
apiEntries: ApiEntry[];
|
|
9
|
+
hasAuthConfig: boolean;
|
|
10
|
+
authConfigPath: string;
|
|
11
|
+
shared: {
|
|
12
|
+
resolve: UserConfig['resolve'];
|
|
13
|
+
esbuild: UserConfig['esbuild'];
|
|
14
|
+
plugins: Plugin[];
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export declare function buildServer(opts: BuildServerOptions): Promise<void>;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { build as viteBuild } from 'vite';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import fs from 'fs';
|
|
4
|
+
export async function buildServer(opts) {
|
|
5
|
+
const { projectDir, serverDir, pageEntries, layoutEntries, apiEntries, hasAuthConfig, authConfigPath, shared } = opts;
|
|
6
|
+
console.log('[LumenJS] Building server bundle...');
|
|
7
|
+
// Collect server entry points (pages with loaders + layouts with loaders + API routes)
|
|
8
|
+
const serverEntries = {};
|
|
9
|
+
for (const entry of pageEntries) {
|
|
10
|
+
if (entry.hasLoader || entry.hasSubscribe || entry.prerender) {
|
|
11
|
+
serverEntries[`pages/${entry.name}`] = entry.filePath;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
for (const entry of layoutEntries) {
|
|
15
|
+
if (entry.hasLoader || entry.hasSubscribe) {
|
|
16
|
+
const entryName = entry.dir ? `layouts/${entry.dir}/_layout` : 'layouts/_layout';
|
|
17
|
+
serverEntries[entryName] = entry.filePath;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
for (const entry of apiEntries) {
|
|
21
|
+
serverEntries[`api/${entry.name}`] = entry.filePath;
|
|
22
|
+
}
|
|
23
|
+
if (hasAuthConfig) {
|
|
24
|
+
serverEntries['auth-config'] = authConfigPath;
|
|
25
|
+
}
|
|
26
|
+
// If data/seed.ts exists, include it in the server bundle for prod seed support
|
|
27
|
+
const seedPath = path.join(projectDir, 'data', 'seed.ts');
|
|
28
|
+
if (fs.existsSync(seedPath)) {
|
|
29
|
+
serverEntries['seed'] = seedPath;
|
|
30
|
+
}
|
|
31
|
+
// Create SSR runtime entry — bundles @lit-labs/ssr alongside Lit so all
|
|
32
|
+
// server modules share one Lit instance (avoids _$EM mismatches).
|
|
33
|
+
const ssrEntryPath = path.join(projectDir, '__nk_ssr_entry.js');
|
|
34
|
+
const hasPageLoaders = pageEntries.some(e => e.hasLoader);
|
|
35
|
+
const hasLayoutLoaders = layoutEntries.some(e => e.hasLoader);
|
|
36
|
+
const hasPrerenderPages = pageEntries.some(e => e.prerender);
|
|
37
|
+
if (hasPageLoaders || hasLayoutLoaders || hasPrerenderPages) {
|
|
38
|
+
fs.writeFileSync(ssrEntryPath, [
|
|
39
|
+
"import '@lit-labs/ssr/lib/install-global-dom-shim.js';",
|
|
40
|
+
"export { render } from '@lit-labs/ssr';",
|
|
41
|
+
"export { html, unsafeStatic } from 'lit/static-html.js';",
|
|
42
|
+
].join('\n'));
|
|
43
|
+
serverEntries['ssr-runtime'] = ssrEntryPath;
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
if (Object.keys(serverEntries).length > 0) {
|
|
47
|
+
await viteBuild({
|
|
48
|
+
root: projectDir,
|
|
49
|
+
resolve: shared.resolve,
|
|
50
|
+
plugins: shared.plugins,
|
|
51
|
+
esbuild: shared.esbuild,
|
|
52
|
+
build: {
|
|
53
|
+
outDir: serverDir,
|
|
54
|
+
emptyOutDir: true,
|
|
55
|
+
ssr: true,
|
|
56
|
+
rollupOptions: {
|
|
57
|
+
input: serverEntries,
|
|
58
|
+
output: {
|
|
59
|
+
format: 'esm',
|
|
60
|
+
entryFileNames: '[name].js',
|
|
61
|
+
chunkFileNames: 'assets/[name]-[hash].js',
|
|
62
|
+
manualChunks(id) {
|
|
63
|
+
// Force all Lit packages into a single shared chunk so SSR runtime
|
|
64
|
+
// and page modules use the exact same Lit class instances.
|
|
65
|
+
if (id.includes('/node_modules/lit/') ||
|
|
66
|
+
id.includes('/node_modules/lit-html/') ||
|
|
67
|
+
id.includes('/node_modules/lit-element/') ||
|
|
68
|
+
id.includes('/node_modules/@lit/reactive-element/')) {
|
|
69
|
+
return 'lit-shared';
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
external: [
|
|
74
|
+
/^node:/,
|
|
75
|
+
'os', 'fs', 'path', 'url', 'util', 'crypto', 'http', 'https', 'net',
|
|
76
|
+
'stream', 'zlib', 'events', 'buffer', 'querystring', 'child_process',
|
|
77
|
+
'worker_threads', 'cluster', 'dns', 'tls', 'assert', 'constants',
|
|
78
|
+
// Native addons — must not be bundled, loaded from node_modules at runtime
|
|
79
|
+
'better-sqlite3',
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
logLevel: 'warn',
|
|
84
|
+
ssr: {
|
|
85
|
+
noExternal: true,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
fs.mkdirSync(serverDir, { recursive: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
finally {
|
|
94
|
+
if (fs.existsSync(ssrEntryPath)) {
|
|
95
|
+
fs.unlinkSync(ssrEntryPath);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
package/dist/build/build.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { build as viteBuild } from 'vite';
|
|
2
1
|
import path from 'path';
|
|
3
2
|
import fs from 'fs';
|
|
4
3
|
import { getSharedViteConfig } from '../dev-server/server.js';
|
|
5
4
|
import { readProjectConfig } from '../dev-server/config.js';
|
|
6
|
-
import { generateIndexHtml } from '../dev-server/index-html.js';
|
|
7
5
|
import { filePathToTagName } from '../shared/utils.js';
|
|
8
6
|
import { scanPages, scanLayouts, scanApiRoutes, getLayoutDirsForPage } from './scan.js';
|
|
7
|
+
import { buildClient } from './build-client.js';
|
|
8
|
+
import { buildServer } from './build-server.js';
|
|
9
|
+
import { prerenderPages } from './build-prerender.js';
|
|
9
10
|
export async function buildProject(options) {
|
|
10
11
|
const { projectDir } = options;
|
|
11
12
|
const outDir = options.outDir || path.join(projectDir, '.lumenjs');
|
|
@@ -19,130 +20,42 @@ export async function buildProject(options) {
|
|
|
19
20
|
fs.rmSync(outDir, { recursive: true });
|
|
20
21
|
}
|
|
21
22
|
fs.mkdirSync(outDir, { recursive: true });
|
|
22
|
-
const { title, integrations, i18n: i18nConfig } = readProjectConfig(projectDir);
|
|
23
|
+
const { title, integrations, i18n: i18nConfig, prefetch: prefetchStrategy, prerender: globalPrerender } = readProjectConfig(projectDir);
|
|
23
24
|
const shared = getSharedViteConfig(projectDir, { mode: 'production', integrations });
|
|
24
25
|
// Scan pages, layouts, and API routes for the manifest
|
|
25
26
|
const pageEntries = scanPages(pagesDir);
|
|
26
27
|
const layoutEntries = scanLayouts(pagesDir);
|
|
27
28
|
const apiEntries = scanApiRoutes(apiDir);
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await viteBuild({
|
|
36
|
-
root: projectDir,
|
|
37
|
-
publicDir: fs.existsSync(publicDir) ? publicDir : undefined,
|
|
38
|
-
resolve: shared.resolve,
|
|
39
|
-
plugins: shared.plugins,
|
|
40
|
-
esbuild: shared.esbuild,
|
|
41
|
-
build: {
|
|
42
|
-
outDir: clientDir,
|
|
43
|
-
emptyOutDir: true,
|
|
44
|
-
rollupOptions: {
|
|
45
|
-
input: tempIndexPath,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
logLevel: 'warn',
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
finally {
|
|
52
|
-
// Clean up temp file
|
|
53
|
-
if (fs.existsSync(tempIndexPath)) {
|
|
54
|
-
fs.unlinkSync(tempIndexPath);
|
|
29
|
+
// Check for auth config
|
|
30
|
+
const authConfigPath = path.join(projectDir, 'lumenjs.auth.ts');
|
|
31
|
+
const hasAuthConfig = fs.existsSync(authConfigPath);
|
|
32
|
+
// Apply global prerender flag from config
|
|
33
|
+
if (globalPrerender) {
|
|
34
|
+
for (const entry of pageEntries) {
|
|
35
|
+
entry.prerender = true;
|
|
55
36
|
}
|
|
56
37
|
}
|
|
57
|
-
//
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
38
|
+
// --- Client build ---
|
|
39
|
+
await buildClient({
|
|
40
|
+
projectDir,
|
|
41
|
+
clientDir,
|
|
42
|
+
title,
|
|
43
|
+
integrations,
|
|
44
|
+
prefetchStrategy,
|
|
45
|
+
publicDir,
|
|
46
|
+
shared,
|
|
47
|
+
});
|
|
63
48
|
// --- Server build ---
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const entryName = entry.dir ? `layouts/${entry.dir}/_layout` : 'layouts/_layout';
|
|
75
|
-
serverEntries[entryName] = entry.filePath;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
for (const entry of apiEntries) {
|
|
79
|
-
serverEntries[`api/${entry.name}`] = entry.filePath;
|
|
80
|
-
}
|
|
81
|
-
// Create SSR runtime entry — bundles @lit-labs/ssr alongside Lit so all
|
|
82
|
-
// server modules share one Lit instance (avoids _$EM mismatches).
|
|
83
|
-
const ssrEntryPath = path.join(projectDir, '__nk_ssr_entry.js');
|
|
84
|
-
const hasPageLoaders = pageEntries.some(e => e.hasLoader);
|
|
85
|
-
const hasLayoutLoaders = layoutEntries.some(e => e.hasLoader);
|
|
86
|
-
if (hasPageLoaders || hasLayoutLoaders) {
|
|
87
|
-
fs.writeFileSync(ssrEntryPath, [
|
|
88
|
-
"import '@lit-labs/ssr/lib/install-global-dom-shim.js';",
|
|
89
|
-
"export { render } from '@lit-labs/ssr';",
|
|
90
|
-
"export { html, unsafeStatic } from 'lit/static-html.js';",
|
|
91
|
-
].join('\n'));
|
|
92
|
-
serverEntries['ssr-runtime'] = ssrEntryPath;
|
|
93
|
-
}
|
|
94
|
-
try {
|
|
95
|
-
if (Object.keys(serverEntries).length > 0) {
|
|
96
|
-
await viteBuild({
|
|
97
|
-
root: projectDir,
|
|
98
|
-
resolve: shared.resolve,
|
|
99
|
-
plugins: shared.plugins,
|
|
100
|
-
esbuild: shared.esbuild,
|
|
101
|
-
build: {
|
|
102
|
-
outDir: serverDir,
|
|
103
|
-
emptyOutDir: true,
|
|
104
|
-
ssr: true,
|
|
105
|
-
rollupOptions: {
|
|
106
|
-
input: serverEntries,
|
|
107
|
-
output: {
|
|
108
|
-
format: 'esm',
|
|
109
|
-
entryFileNames: '[name].js',
|
|
110
|
-
chunkFileNames: 'assets/[name]-[hash].js',
|
|
111
|
-
manualChunks(id) {
|
|
112
|
-
// Force all Lit packages into a single shared chunk so SSR runtime
|
|
113
|
-
// and page modules use the exact same Lit class instances.
|
|
114
|
-
if (id.includes('/node_modules/lit/') ||
|
|
115
|
-
id.includes('/node_modules/lit-html/') ||
|
|
116
|
-
id.includes('/node_modules/lit-element/') ||
|
|
117
|
-
id.includes('/node_modules/@lit/reactive-element/')) {
|
|
118
|
-
return 'lit-shared';
|
|
119
|
-
}
|
|
120
|
-
},
|
|
121
|
-
},
|
|
122
|
-
external: [
|
|
123
|
-
/^node:/,
|
|
124
|
-
'os', 'fs', 'path', 'url', 'util', 'crypto', 'http', 'https', 'net',
|
|
125
|
-
'stream', 'zlib', 'events', 'buffer', 'querystring', 'child_process',
|
|
126
|
-
'worker_threads', 'cluster', 'dns', 'tls', 'assert', 'constants',
|
|
127
|
-
'better-sqlite3',
|
|
128
|
-
],
|
|
129
|
-
},
|
|
130
|
-
},
|
|
131
|
-
logLevel: 'warn',
|
|
132
|
-
ssr: {
|
|
133
|
-
noExternal: true,
|
|
134
|
-
},
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
fs.mkdirSync(serverDir, { recursive: true });
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
finally {
|
|
142
|
-
if (fs.existsSync(ssrEntryPath)) {
|
|
143
|
-
fs.unlinkSync(ssrEntryPath);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
49
|
+
await buildServer({
|
|
50
|
+
projectDir,
|
|
51
|
+
serverDir,
|
|
52
|
+
pageEntries,
|
|
53
|
+
layoutEntries,
|
|
54
|
+
apiEntries,
|
|
55
|
+
hasAuthConfig,
|
|
56
|
+
authConfigPath,
|
|
57
|
+
shared,
|
|
58
|
+
});
|
|
146
59
|
// --- Copy locales ---
|
|
147
60
|
if (i18nConfig) {
|
|
148
61
|
const localesDir = path.join(projectDir, 'locales');
|
|
@@ -160,15 +73,19 @@ export async function buildProject(options) {
|
|
|
160
73
|
// --- Write manifest ---
|
|
161
74
|
const manifest = {
|
|
162
75
|
routes: pageEntries.map(e => {
|
|
163
|
-
const routeLayouts = getLayoutDirsForPage(e.filePath, pagesDir, layoutEntries);
|
|
76
|
+
const routeLayouts = e.hasStandalone ? [] : getLayoutDirsForPage(e.filePath, pagesDir, layoutEntries);
|
|
164
77
|
const relPath = path.relative(pagesDir, e.filePath).replace(/\\/g, '/');
|
|
165
78
|
return {
|
|
166
79
|
path: e.routePath,
|
|
167
|
-
module: (e.hasLoader || e.hasSubscribe) ? `pages/${e.name}.js` : '',
|
|
80
|
+
module: (e.hasLoader || e.hasSubscribe || e.prerender) ? `pages/${e.name}.js` : '',
|
|
168
81
|
hasLoader: e.hasLoader,
|
|
169
82
|
hasSubscribe: e.hasSubscribe,
|
|
170
83
|
tagName: filePathToTagName(relPath),
|
|
171
84
|
...(routeLayouts.length > 0 ? { layouts: routeLayouts } : {}),
|
|
85
|
+
...(e.hasAuth ? { hasAuth: true } : {}),
|
|
86
|
+
...(e.hasMeta ? { hasMeta: true } : {}),
|
|
87
|
+
...(e.hasStandalone ? { hasStandalone: true } : {}),
|
|
88
|
+
...(e.prerender ? { prerender: true } : {}),
|
|
172
89
|
};
|
|
173
90
|
}),
|
|
174
91
|
apiRoutes: apiEntries.map(e => ({
|
|
@@ -184,8 +101,19 @@ export async function buildProject(options) {
|
|
|
184
101
|
hasSubscribe: e.hasSubscribe,
|
|
185
102
|
})),
|
|
186
103
|
...(i18nConfig ? { i18n: i18nConfig } : {}),
|
|
104
|
+
...(hasAuthConfig ? { auth: { configModule: 'auth-config.js' } } : {}),
|
|
105
|
+
prefetch: prefetchStrategy,
|
|
187
106
|
};
|
|
188
107
|
fs.writeFileSync(path.join(outDir, 'manifest.json'), JSON.stringify(manifest, null, 2));
|
|
108
|
+
// --- Pre-render phase ---
|
|
109
|
+
await prerenderPages({
|
|
110
|
+
serverDir,
|
|
111
|
+
clientDir,
|
|
112
|
+
pagesDir,
|
|
113
|
+
pageEntries,
|
|
114
|
+
layoutEntries,
|
|
115
|
+
manifest,
|
|
116
|
+
});
|
|
189
117
|
console.log('[LumenJS] Build complete.');
|
|
190
118
|
console.log(` Output: ${outDir}`);
|
|
191
119
|
console.log(` Client assets: ${clientDir}`);
|
package/dist/build/scan.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ export interface PageEntry {
|
|
|
4
4
|
routePath: string;
|
|
5
5
|
hasLoader: boolean;
|
|
6
6
|
hasSubscribe: boolean;
|
|
7
|
+
hasAuth: boolean;
|
|
8
|
+
hasMeta: boolean;
|
|
9
|
+
hasStandalone: boolean;
|
|
10
|
+
prerender: boolean;
|
|
7
11
|
}
|
|
8
12
|
export interface LayoutEntry {
|
|
9
13
|
dir: string;
|
|
@@ -21,3 +25,16 @@ export declare function scanLayouts(pagesDir: string): LayoutEntry[];
|
|
|
21
25
|
export declare function scanApiRoutes(apiDir: string): ApiEntry[];
|
|
22
26
|
/** Get the layout directory chain for a given page file */
|
|
23
27
|
export declare function getLayoutDirsForPage(pageFilePath: string, pagesDir: string, layouts: LayoutEntry[]): string[];
|
|
28
|
+
export interface MiddlewareEntry {
|
|
29
|
+
dir: string;
|
|
30
|
+
filePath: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Scan for _middleware.ts files in the pages directory tree.
|
|
34
|
+
*/
|
|
35
|
+
export declare function scanMiddleware(pagesDir: string): MiddlewareEntry[];
|
|
36
|
+
/**
|
|
37
|
+
* Get middleware directories that match a given URL pathname.
|
|
38
|
+
* Returns matching middleware entries from root → deepest.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getMiddlewareDirsForPathname(pathname: string, entries: MiddlewareEntry[]): MiddlewareEntry[];
|
package/dist/build/scan.js
CHANGED
|
@@ -1,6 +1,32 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { fileHasLoader, fileHasSubscribe
|
|
3
|
+
import { filePathToRoute, fileHasLoader, fileHasSubscribe } from '../shared/utils.js';
|
|
4
|
+
/** Read a page file once and check all flags from the same content. */
|
|
5
|
+
function analyzePageFile(filePath) {
|
|
6
|
+
try {
|
|
7
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
8
|
+
const classStart = content.search(/export\s+class\s+\w+/);
|
|
9
|
+
const hasExportBefore = (regex) => {
|
|
10
|
+
const match = regex.exec(content);
|
|
11
|
+
if (!match)
|
|
12
|
+
return false;
|
|
13
|
+
if (classStart >= 0 && match.index > classStart)
|
|
14
|
+
return false;
|
|
15
|
+
return true;
|
|
16
|
+
};
|
|
17
|
+
return {
|
|
18
|
+
hasLoader: hasExportBefore(/export\s+(async\s+)?function\s+loader\s*\(/),
|
|
19
|
+
hasSubscribe: hasExportBefore(/export\s+(async\s+)?function\s+subscribe\s*\(/),
|
|
20
|
+
hasAuth: hasExportBefore(/export\s+const\s+auth\s*=/),
|
|
21
|
+
hasMeta: hasExportBefore(/export\s+(const\s+meta\s*=|(async\s+)?function\s+meta\s*\()/),
|
|
22
|
+
hasStandalone: hasExportBefore(/export\s+const\s+standalone\s*=/),
|
|
23
|
+
prerender: /export\s+const\s+prerender\s*=\s*true/.test(content),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return { hasLoader: false, hasSubscribe: false, hasAuth: false, hasMeta: false, hasStandalone: false, prerender: false };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
4
30
|
export function scanPages(pagesDir) {
|
|
5
31
|
if (!fs.existsSync(pagesDir))
|
|
6
32
|
return [];
|
|
@@ -44,18 +70,23 @@ export function getLayoutDirsForPage(pageFilePath, pagesDir, layouts) {
|
|
|
44
70
|
function walkDir(baseDir, relativePath, entries, pagesDir) {
|
|
45
71
|
const fullDir = path.join(baseDir, relativePath);
|
|
46
72
|
const dirEntries = fs.readdirSync(fullDir, { withFileTypes: true });
|
|
73
|
+
// Check if this subdirectory contains an index file (folder route)
|
|
74
|
+
// Only applies to subdirectories, not the root pages directory
|
|
75
|
+
const hasIndex = relativePath !== '' && dirEntries.some(e => e.isFile() && /^index\.(ts|js)$/.test(e.name));
|
|
47
76
|
for (const entry of dirEntries) {
|
|
48
77
|
const entryRelative = path.join(relativePath, entry.name);
|
|
49
|
-
if (entry.isDirectory()) {
|
|
78
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
50
79
|
walkDir(baseDir, entryRelative, entries, pagesDir);
|
|
51
80
|
}
|
|
52
81
|
else if (entry.isFile() && /\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('_')) {
|
|
82
|
+
// In a folder route (has index file), only register the index file and dynamic param files
|
|
83
|
+
if (hasIndex && !/^index\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('['))
|
|
84
|
+
continue;
|
|
53
85
|
const filePath = path.join(pagesDir, entryRelative);
|
|
54
86
|
const name = entryRelative.replace(/\.(ts|js)$/, '').replace(/\\/g, '/');
|
|
55
87
|
const routePath = filePathToRoute(entryRelative);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
entries.push({ name, filePath, routePath, hasLoader, hasSubscribe });
|
|
88
|
+
const flags = analyzePageFile(filePath);
|
|
89
|
+
entries.push({ name, filePath, routePath, ...flags });
|
|
59
90
|
}
|
|
60
91
|
}
|
|
61
92
|
}
|
|
@@ -68,11 +99,50 @@ function walkForLayouts(baseDir, relativePath, entries) {
|
|
|
68
99
|
const dir = relativePath.replace(/\\/g, '/');
|
|
69
100
|
entries.push({ dir, filePath, hasLoader: fileHasLoader(filePath), hasSubscribe: fileHasSubscribe(filePath) });
|
|
70
101
|
}
|
|
71
|
-
if (entry.isDirectory()) {
|
|
102
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
72
103
|
walkForLayouts(baseDir, path.join(relativePath, entry.name), entries);
|
|
73
104
|
}
|
|
74
105
|
}
|
|
75
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Scan for _middleware.ts files in the pages directory tree.
|
|
109
|
+
*/
|
|
110
|
+
export function scanMiddleware(pagesDir) {
|
|
111
|
+
if (!fs.existsSync(pagesDir))
|
|
112
|
+
return [];
|
|
113
|
+
const entries = [];
|
|
114
|
+
walkForMiddleware(pagesDir, '', entries);
|
|
115
|
+
return entries;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Get middleware directories that match a given URL pathname.
|
|
119
|
+
* Returns matching middleware entries from root → deepest.
|
|
120
|
+
*/
|
|
121
|
+
export function getMiddlewareDirsForPathname(pathname, entries) {
|
|
122
|
+
const urlSegments = pathname.replace(/^\//, '').split('/').filter(Boolean);
|
|
123
|
+
return entries.filter(entry => {
|
|
124
|
+
if (entry.dir === '')
|
|
125
|
+
return true; // Root middleware applies to all routes
|
|
126
|
+
const dirSegments = entry.dir.split('/').filter(Boolean);
|
|
127
|
+
if (dirSegments.length > urlSegments.length)
|
|
128
|
+
return false;
|
|
129
|
+
return dirSegments.every((seg, i) => seg === urlSegments[i]);
|
|
130
|
+
}).sort((a, b) => a.dir.split('/').length - b.dir.split('/').length);
|
|
131
|
+
}
|
|
132
|
+
function walkForMiddleware(baseDir, relativePath, entries) {
|
|
133
|
+
const fullDir = path.join(baseDir, relativePath);
|
|
134
|
+
const dirEntries = fs.readdirSync(fullDir, { withFileTypes: true });
|
|
135
|
+
for (const entry of dirEntries) {
|
|
136
|
+
if (entry.isFile() && /^_middleware\.(ts|js)$/.test(entry.name)) {
|
|
137
|
+
const filePath = path.join(fullDir, entry.name);
|
|
138
|
+
const dir = relativePath.replace(/\\/g, '/');
|
|
139
|
+
entries.push({ dir, filePath });
|
|
140
|
+
}
|
|
141
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
142
|
+
walkForMiddleware(baseDir, path.join(relativePath, entry.name), entries);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
76
146
|
function walkApiDir(baseDir, relativePath, entries, apiDir) {
|
|
77
147
|
const fullDir = path.join(baseDir, relativePath);
|
|
78
148
|
const dirEntries = fs.readdirSync(fullDir, { withFileTypes: true });
|
package/dist/build/serve-api.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { readBody } from '../shared/utils.js';
|
|
4
4
|
import { matchRoute } from '../shared/route-matching.js';
|
|
5
|
+
import { useStorage } from '../storage/index.js';
|
|
5
6
|
export async function handleApiRoute(manifest, serverDir, pathname, queryString, method, req, res) {
|
|
6
7
|
const matched = matchRoute(manifest.apiRoutes, pathname);
|
|
7
8
|
if (!matched) {
|
|
@@ -9,7 +10,9 @@ export async function handleApiRoute(manifest, serverDir, pathname, queryString,
|
|
|
9
10
|
res.end(JSON.stringify({ error: 'API route not found' }));
|
|
10
11
|
return;
|
|
11
12
|
}
|
|
12
|
-
|
|
13
|
+
// Vite replaces [param] with _param_ in output filenames
|
|
14
|
+
const resolvedModule = matched.route.module.replace(/\[([^\]]+)\]/g, '_$1_');
|
|
15
|
+
const modulePath = path.join(serverDir, resolvedModule);
|
|
13
16
|
if (!fs.existsSync(modulePath)) {
|
|
14
17
|
res.writeHead(404, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
15
18
|
res.end(JSON.stringify({ error: 'API route module not found' }));
|
|
@@ -43,13 +46,16 @@ export async function handleApiRoute(manifest, serverDir, pathname, queryString,
|
|
|
43
46
|
params: matched.params,
|
|
44
47
|
body,
|
|
45
48
|
headers: req.headers,
|
|
49
|
+
storage: useStorage(),
|
|
50
|
+
nkAuth: req.nkAuth,
|
|
46
51
|
});
|
|
47
52
|
res.writeHead(200, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
48
53
|
res.end(JSON.stringify(result));
|
|
49
54
|
}
|
|
50
55
|
catch (err) {
|
|
51
56
|
const status = err?.status || 500;
|
|
52
|
-
|
|
57
|
+
// Only expose error messages for client errors (4xx); hide internals for 5xx
|
|
58
|
+
const message = status < 500 ? (err?.message || 'Request error') : 'Internal server error';
|
|
53
59
|
res.writeHead(status, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
54
60
|
res.end(JSON.stringify({ error: message }));
|
|
55
61
|
}
|