@nuraly/lumenjs 0.1.3 → 0.2.0
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 +82 -0
- package/dist/auth/native-auth.js +340 -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 +121 -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/totp.d.ts +22 -0
- package/dist/auth/routes/totp.js +232 -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 +124 -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 +18 -0
- package/dist/build/build-server.js +107 -0
- package/dist/build/build.js +60 -123
- package/dist/build/scan.d.ts +18 -0
- package/dist/build/scan.js +77 -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 +341 -18
- 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 +40 -20
- package/dist/dev-server/index-html.d.ts +4 -0
- package/dist/dev-server/index-html.js +21 -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 +16 -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 +140 -3
- package/dist/dev-server/server.js +242 -70
- package/dist/dev-server/ssr-render.d.ts +2 -1
- package/dist/dev-server/ssr-render.js +117 -50
- package/dist/editor/ai/backend.d.ts +20 -0
- package/dist/editor/ai/backend.js +113 -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/deepseek-client.d.ts +7 -0
- package/dist/editor/ai/deepseek-client.js +113 -0
- package/dist/editor/ai/opencode-client.d.ts +14 -0
- package/dist/editor/ai/opencode-client.js +99 -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 +613 -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 +18 -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 +76 -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.d.ts +1 -1
- package/dist/runtime/app-shell.js +164 -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/island.d.ts +16 -0
- package/dist/runtime/island.js +80 -0
- package/dist/runtime/router-data.d.ts +3 -0
- package/dist/runtime/router-data.js +102 -17
- package/dist/runtime/router-hydration.js +34 -2
- package/dist/runtime/router.d.ts +19 -2
- package/dist/runtime/router.js +237 -43
- package/dist/runtime/socket-client.d.ts +2 -0
- package/dist/runtime/socket-client.js +30 -0
- package/dist/runtime/webrtc.d.ts +91 -0
- package/dist/runtime/webrtc.js +428 -0
- package/dist/shared/dom-shims.js +4 -2
- 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 +15 -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 +119 -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 +45 -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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
|
-
import { dirToLayoutTagName, fileHasLoader, fileHasSubscribe, filePathToRoute, filePathToTagName } from '../../shared/utils.js';
|
|
3
|
+
import { dirToLayoutTagName, fileHasLoader, fileHasSubscribe, fileHasSocket, fileHasAuth, fileHasMeta, fileHasStandalone, filePathToRoute, filePathToTagName } from '../../shared/utils.js';
|
|
4
4
|
const VIRTUAL_MODULE_ID = 'virtual:lumenjs-routes';
|
|
5
5
|
const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
|
|
6
6
|
/**
|
|
@@ -28,7 +28,7 @@ export function lumenRoutesPlugin(pagesDir) {
|
|
|
28
28
|
const tagName = dirToLayoutTagName(relativePath);
|
|
29
29
|
layouts.push({ dir: relativePath.replace(/\\/g, '/'), filePath, tagName });
|
|
30
30
|
}
|
|
31
|
-
if (entry.isDirectory()) {
|
|
31
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
32
32
|
walkForLayouts(baseDir, path.join(relativePath, entry.name), layouts);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
@@ -55,12 +55,18 @@ export function lumenRoutesPlugin(pagesDir) {
|
|
|
55
55
|
function walkDir(baseDir, relativePath, routes) {
|
|
56
56
|
const fullDir = path.join(baseDir, relativePath);
|
|
57
57
|
const entries = fs.readdirSync(fullDir, { withFileTypes: true });
|
|
58
|
+
// Check if this subdirectory contains an index file (folder route)
|
|
59
|
+
// Only applies to subdirectories, not the root pages directory
|
|
60
|
+
const hasIndex = relativePath !== '' && entries.some(e => e.isFile() && /^index\.(ts|js)$/.test(e.name));
|
|
58
61
|
for (const entry of entries) {
|
|
59
62
|
const entryRelative = path.join(relativePath, entry.name);
|
|
60
|
-
if (entry.isDirectory()) {
|
|
63
|
+
if (entry.isDirectory() && !entry.name.startsWith('_')) {
|
|
61
64
|
walkDir(baseDir, entryRelative, routes);
|
|
62
65
|
}
|
|
63
66
|
else if (entry.isFile() && /\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('_')) {
|
|
67
|
+
// In a folder route (has index file), only register the index file and dynamic param files
|
|
68
|
+
if (hasIndex && !/^index\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('['))
|
|
69
|
+
continue;
|
|
64
70
|
const routePath = filePathToRoute(entryRelative);
|
|
65
71
|
const componentPath = path.join(pagesDir, entryRelative);
|
|
66
72
|
const tagName = filePathToTagName(entryRelative);
|
|
@@ -99,8 +105,13 @@ export function lumenRoutesPlugin(pagesDir) {
|
|
|
99
105
|
.map(r => {
|
|
100
106
|
const hasLoader = fileHasLoader(r.componentPath);
|
|
101
107
|
const hasSubscribe = fileHasSubscribe(r.componentPath);
|
|
108
|
+
const hasSocketFlag = fileHasSocket(r.componentPath);
|
|
109
|
+
const hasAuth = fileHasAuth(r.componentPath);
|
|
110
|
+
const hasMeta = fileHasMeta(r.componentPath);
|
|
111
|
+
const isStandalone = fileHasStandalone(r.componentPath);
|
|
102
112
|
const componentPath = r.componentPath.replace(/\\/g, '/');
|
|
103
|
-
|
|
113
|
+
// Standalone pages skip all layouts
|
|
114
|
+
const chain = isStandalone ? [] : getLayoutChain(r.componentPath, layouts);
|
|
104
115
|
let layoutsStr = '';
|
|
105
116
|
if (chain.length > 0) {
|
|
106
117
|
const items = chain.map(l => {
|
|
@@ -111,7 +122,7 @@ export function lumenRoutesPlugin(pagesDir) {
|
|
|
111
122
|
});
|
|
112
123
|
layoutsStr = `, layouts: [${items.join(', ')}]`;
|
|
113
124
|
}
|
|
114
|
-
return ` { path: ${JSON.stringify(r.path)}, tagName: ${JSON.stringify(r.tagName)}${hasLoader ? ', hasLoader: true' : ''}${hasSubscribe ? ', hasSubscribe: true' : ''}, load: () => import('${componentPath}')${layoutsStr} }`;
|
|
125
|
+
return ` { path: ${JSON.stringify(r.path)}, tagName: ${JSON.stringify(r.tagName)}${hasLoader ? ', hasLoader: true' : ''}${hasSubscribe ? ', hasSubscribe: true' : ''}${hasSocketFlag ? ', hasSocket: true' : ''}${hasMeta ? ', hasMeta: true' : ''}${hasAuth ? ', __nk_has_auth: true' : ''}, load: () => import('${componentPath}')${layoutsStr} }`;
|
|
115
126
|
})
|
|
116
127
|
.join(',\n');
|
|
117
128
|
return `export const routes = [\n${routeArray}\n];\n`;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileHasSocket, filePathToRoute } from '../../shared/utils.js';
|
|
4
|
+
export function lumenSocketIOPlugin(pagesDir) {
|
|
5
|
+
return {
|
|
6
|
+
name: 'lumenjs-socketio',
|
|
7
|
+
configureServer(server) {
|
|
8
|
+
if (!server.httpServer)
|
|
9
|
+
return;
|
|
10
|
+
import('../../shared/socket-io-setup.js').then(({ setupSocketIO }) => {
|
|
11
|
+
const routes = scanSocketRoutes(pagesDir);
|
|
12
|
+
setupSocketIO({
|
|
13
|
+
httpServer: server.httpServer,
|
|
14
|
+
loadModule: (fp) => server.ssrLoadModule(fp),
|
|
15
|
+
routes,
|
|
16
|
+
projectDir: path.dirname(pagesDir),
|
|
17
|
+
}).catch((err) => {
|
|
18
|
+
console.warn('[LumenJS] Socket.IO setup failed:', err.message);
|
|
19
|
+
console.warn('[LumenJS] Make sure socket.io is installed: lumenjs add socketio');
|
|
20
|
+
});
|
|
21
|
+
}).catch((err) => {
|
|
22
|
+
console.warn('[LumenJS] Socket.IO plugin load failed:', err.message);
|
|
23
|
+
});
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
function scanSocketRoutes(pagesDir) {
|
|
28
|
+
const routes = [];
|
|
29
|
+
if (!fs.existsSync(pagesDir))
|
|
30
|
+
return routes;
|
|
31
|
+
walkDir(pagesDir, '', routes, pagesDir);
|
|
32
|
+
return routes;
|
|
33
|
+
}
|
|
34
|
+
function walkDir(baseDir, relativePath, routes, pagesDir) {
|
|
35
|
+
const fullDir = path.join(baseDir, relativePath);
|
|
36
|
+
const entries = fs.readdirSync(fullDir, { withFileTypes: true });
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const entryRelative = path.join(relativePath, entry.name);
|
|
39
|
+
if (entry.isDirectory()) {
|
|
40
|
+
walkDir(baseDir, entryRelative, routes, pagesDir);
|
|
41
|
+
}
|
|
42
|
+
else if (entry.isFile() && /\.(ts|js)$/.test(entry.name) && !entry.name.startsWith('_')) {
|
|
43
|
+
const filePath = path.join(pagesDir, entryRelative);
|
|
44
|
+
const hasSocket = fileHasSocket(filePath);
|
|
45
|
+
if (hasSocket) {
|
|
46
|
+
const routePath = filePathToRoute(entryRelative);
|
|
47
|
+
routes.push({ path: routePath, hasSocket: true, filePath });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Plugin } from 'vite';
|
|
2
2
|
/**
|
|
3
3
|
* In editor mode, inject data-nk-source attributes into html`` template literals.
|
|
4
|
+
* Reads the original source file from disk to compute correct line numbers,
|
|
5
|
+
* since Vite's transform hook receives esbuild-compiled code with shifted lines.
|
|
4
6
|
*/
|
|
5
7
|
export declare function sourceAnnotatorPlugin(projectDir: string): Plugin;
|
|
@@ -1,21 +1,44 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
2
3
|
/**
|
|
3
4
|
* In editor mode, inject data-nk-source attributes into html`` template literals.
|
|
5
|
+
* Reads the original source file from disk to compute correct line numbers,
|
|
6
|
+
* since Vite's transform hook receives esbuild-compiled code with shifted lines.
|
|
4
7
|
*/
|
|
5
8
|
export function sourceAnnotatorPlugin(projectDir) {
|
|
6
9
|
return {
|
|
7
10
|
name: 'lumenjs-source-annotator',
|
|
11
|
+
enforce: 'pre',
|
|
8
12
|
transform(code, id) {
|
|
9
13
|
if (!id.startsWith(projectDir) || !id.endsWith('.ts'))
|
|
10
14
|
return;
|
|
11
15
|
if (!code.includes('html`'))
|
|
12
16
|
return;
|
|
17
|
+
// Read the original source to get correct line numbers
|
|
18
|
+
let originalSource;
|
|
19
|
+
try {
|
|
20
|
+
originalSource = fs.readFileSync(id, 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
originalSource = code;
|
|
24
|
+
}
|
|
13
25
|
const relativePath = path.relative(projectDir, id);
|
|
26
|
+
// Build ordered list of html` base lines from original source
|
|
27
|
+
const originalBaseLines = [];
|
|
28
|
+
const origRegex = /html`/g;
|
|
29
|
+
let origMatch;
|
|
30
|
+
while ((origMatch = origRegex.exec(originalSource)) !== null) {
|
|
31
|
+
const before = originalSource.substring(0, origMatch.index + 5); // include 'html`'
|
|
32
|
+
originalBaseLines.push(before.split('\n').length);
|
|
33
|
+
}
|
|
34
|
+
let templateIndex = 0;
|
|
14
35
|
const transformed = code.replace(/html`([\s\S]*?)`/g, (match, templateContent) => {
|
|
15
36
|
let offset = 0;
|
|
16
|
-
|
|
17
|
-
const baseLine =
|
|
18
|
-
|
|
37
|
+
// Use original source line for this Nth html` template
|
|
38
|
+
const baseLine = originalBaseLines[templateIndex] ?? code.substring(0, code.indexOf(match)).split('\n').length;
|
|
39
|
+
templateIndex++;
|
|
40
|
+
// Annotate both custom elements (tags with hyphens) and standard HTML elements
|
|
41
|
+
const annotated = templateContent.replace(/<((?:[a-z][a-z0-9]*-[a-z0-9-]*)|(?:div|section|article|aside|main|nav|header|footer|h[1-6]|p|span|a|ul|ol|li|button|form|input|textarea|select|label|img|table|tr|td|th|thead|tbody))([\s>])/gi, (tagMatch, tagName, after) => {
|
|
19
42
|
const beforeTag = templateContent.substring(0, templateContent.indexOf(tagMatch, offset));
|
|
20
43
|
const lineInTemplate = beforeTag.split('\n').length - 1;
|
|
21
44
|
offset = templateContent.indexOf(tagMatch, offset) + tagMatch.length;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
/**
|
|
3
|
+
* LumenJS storage plugin (dev mode).
|
|
4
|
+
*
|
|
5
|
+
* - Creates a LocalStorageAdapter pointing to `{projectDir}/uploads`
|
|
6
|
+
* - Registers it as the global storage singleton (`useStorage()`)
|
|
7
|
+
* - Serves uploaded files at `/uploads/*`
|
|
8
|
+
* - Handles presigned PUT requests at `/__nk_storage/upload/:token`
|
|
9
|
+
*/
|
|
10
|
+
export declare function lumenStoragePlugin(projectDir: string): Plugin;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import path from 'path';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import { LocalStorageAdapter } from '../../storage/adapters/local.js';
|
|
4
|
+
import { setStorage } from '../../storage/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* LumenJS storage plugin (dev mode).
|
|
7
|
+
*
|
|
8
|
+
* - Creates a LocalStorageAdapter pointing to `{projectDir}/uploads`
|
|
9
|
+
* - Registers it as the global storage singleton (`useStorage()`)
|
|
10
|
+
* - Serves uploaded files at `/uploads/*`
|
|
11
|
+
* - Handles presigned PUT requests at `/__nk_storage/upload/:token`
|
|
12
|
+
*/
|
|
13
|
+
export function lumenStoragePlugin(projectDir) {
|
|
14
|
+
const uploadDir = path.join(projectDir, 'uploads');
|
|
15
|
+
const adapter = new LocalStorageAdapter({ uploadDir, publicPath: '/uploads' });
|
|
16
|
+
// Register global singleton so API routes and communication handlers can use it
|
|
17
|
+
setStorage(adapter);
|
|
18
|
+
return {
|
|
19
|
+
name: 'lumenjs-storage',
|
|
20
|
+
configureServer(server) {
|
|
21
|
+
// ── Presigned upload endpoint ──────────────────────────────
|
|
22
|
+
// Client sends a PUT to /__nk_storage/upload/:token with the (possibly
|
|
23
|
+
// encrypted) file body. The token was issued via adapter.presignPut().
|
|
24
|
+
server.middlewares.use('/__nk_storage/upload', async (req, res, next) => {
|
|
25
|
+
if (req.method !== 'PUT')
|
|
26
|
+
return next();
|
|
27
|
+
const token = req.url?.replace(/^\//, '').split('?')[0];
|
|
28
|
+
if (!token) {
|
|
29
|
+
res.statusCode = 400;
|
|
30
|
+
res.end(JSON.stringify({ error: 'missing token' }));
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
const pending = adapter.consumeUpload(token);
|
|
34
|
+
if (!pending) {
|
|
35
|
+
res.statusCode = 410; // Gone — expired or unknown token
|
|
36
|
+
res.setHeader('Content-Type', 'application/json');
|
|
37
|
+
res.end(JSON.stringify({ error: 'upload token expired or invalid' }));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Read raw body with streaming size enforcement
|
|
41
|
+
const MAX_UPLOAD = pending.maxSize || 50 * 1024 * 1024; // 50MB default cap
|
|
42
|
+
const chunks = [];
|
|
43
|
+
let uploadSize = 0;
|
|
44
|
+
let aborted = false;
|
|
45
|
+
req.on('data', (c) => {
|
|
46
|
+
uploadSize += c.length;
|
|
47
|
+
if (uploadSize > MAX_UPLOAD) {
|
|
48
|
+
aborted = true;
|
|
49
|
+
req.destroy();
|
|
50
|
+
res.statusCode = 413;
|
|
51
|
+
res.setHeader('Content-Type', 'application/json');
|
|
52
|
+
res.end(JSON.stringify({ error: 'file_too_large', maxSize: MAX_UPLOAD, received: uploadSize }));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
chunks.push(c);
|
|
56
|
+
});
|
|
57
|
+
await new Promise((resolve, reject) => {
|
|
58
|
+
req.on('end', resolve);
|
|
59
|
+
req.on('error', reject);
|
|
60
|
+
});
|
|
61
|
+
if (aborted)
|
|
62
|
+
return;
|
|
63
|
+
const body = Buffer.concat(chunks);
|
|
64
|
+
// Enforce maxSize if specified (redundant safety check)
|
|
65
|
+
if (pending.maxSize && body.length > pending.maxSize) {
|
|
66
|
+
res.statusCode = 413;
|
|
67
|
+
res.setHeader('Content-Type', 'application/json');
|
|
68
|
+
res.end(JSON.stringify({
|
|
69
|
+
error: `file_too_large`,
|
|
70
|
+
maxSize: pending.maxSize,
|
|
71
|
+
received: body.length,
|
|
72
|
+
}));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
// Write to disk (validate path stays within uploadDir)
|
|
76
|
+
const filePath = path.resolve(uploadDir, pending.key);
|
|
77
|
+
if (!filePath.startsWith(uploadDir + path.sep) && filePath !== uploadDir) {
|
|
78
|
+
res.statusCode = 400;
|
|
79
|
+
res.setHeader('Content-Type', 'application/json');
|
|
80
|
+
res.end(JSON.stringify({ error: 'invalid storage key' }));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
84
|
+
fs.writeFileSync(filePath, body);
|
|
85
|
+
res.statusCode = 200;
|
|
86
|
+
res.setHeader('Content-Type', 'application/json');
|
|
87
|
+
res.end(JSON.stringify({
|
|
88
|
+
key: pending.key,
|
|
89
|
+
url: adapter.publicUrl(pending.key),
|
|
90
|
+
size: body.length,
|
|
91
|
+
}));
|
|
92
|
+
});
|
|
93
|
+
// ── Static file serving ────────────────────────────────────
|
|
94
|
+
// Serve files from uploadDir at /uploads/*
|
|
95
|
+
server.middlewares.use('/uploads', (req, res, next) => {
|
|
96
|
+
if (req.method !== 'GET' && req.method !== 'HEAD')
|
|
97
|
+
return next();
|
|
98
|
+
const filePath = path.resolve(uploadDir, (req.url?.split('?')[0] ?? '/').replace(/^\/+/, ''));
|
|
99
|
+
if (!filePath.startsWith(uploadDir + path.sep) && filePath !== uploadDir) {
|
|
100
|
+
return next();
|
|
101
|
+
}
|
|
102
|
+
if (!fs.existsSync(filePath) || fs.statSync(filePath).isDirectory()) {
|
|
103
|
+
return next();
|
|
104
|
+
}
|
|
105
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
106
|
+
const mimeTypes = {
|
|
107
|
+
'.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.png': 'image/png',
|
|
108
|
+
'.gif': 'image/gif', '.webp': 'image/webp', '.svg': 'image/svg+xml',
|
|
109
|
+
'.mp4': 'video/mp4', '.webm': 'video/webm', '.mp3': 'audio/mpeg',
|
|
110
|
+
'.pdf': 'application/pdf', '.json': 'application/json',
|
|
111
|
+
'.txt': 'text/plain', '.bin': 'application/octet-stream',
|
|
112
|
+
};
|
|
113
|
+
const contentType = mimeTypes[ext] ?? 'application/octet-stream';
|
|
114
|
+
const stat = fs.statSync(filePath);
|
|
115
|
+
res.setHeader('Content-Type', contentType);
|
|
116
|
+
res.setHeader('Content-Length', stat.size);
|
|
117
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
118
|
+
if (req.method === 'HEAD') {
|
|
119
|
+
res.end();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
fs.createReadStream(filePath).pipe(res);
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
@@ -15,16 +15,49 @@ export function virtualModulesPlugin(runtimeDir, editorDir) {
|
|
|
15
15
|
'router-data': 'router-data.js',
|
|
16
16
|
'router-hydration': 'router-hydration.js',
|
|
17
17
|
'i18n': 'i18n.js',
|
|
18
|
+
'auth': 'auth.js',
|
|
19
|
+
'communication': 'communication.js',
|
|
20
|
+
'webrtc': 'webrtc.js',
|
|
21
|
+
'error-boundary': 'error-boundary.js',
|
|
22
|
+
'island': 'island.js',
|
|
23
|
+
'hydrate-support': '__virtual__',
|
|
18
24
|
};
|
|
19
25
|
// Modules resolved via resolve.alias instead of virtual module.
|
|
20
26
|
// They still appear in the map so relative import rewrites work.
|
|
21
|
-
const aliasedModules = new Set(['i18n']);
|
|
27
|
+
const aliasedModules = new Set(['i18n', 'auth', 'communication', 'webrtc']);
|
|
22
28
|
const editorModules = {
|
|
23
29
|
'editor-bridge': 'editor-bridge.js',
|
|
24
30
|
'element-annotator': 'element-annotator.js',
|
|
25
31
|
'click-select': 'click-select.js',
|
|
26
32
|
'hover-detect': 'hover-detect.js',
|
|
27
33
|
'inline-text-edit': 'inline-text-edit.js',
|
|
34
|
+
'editor-api-client': 'editor-api-client.js',
|
|
35
|
+
'standalone-overlay': 'standalone-overlay.js',
|
|
36
|
+
'standalone-overlay-dom': 'standalone-overlay-dom.js',
|
|
37
|
+
'standalone-overlay-styles': 'standalone-overlay-styles.js',
|
|
38
|
+
'standalone-file-panel': 'standalone-file-panel.js',
|
|
39
|
+
'overlay-utils': 'overlay-utils.js',
|
|
40
|
+
'overlay-events': 'overlay-events.js',
|
|
41
|
+
'overlay-hmr': 'overlay-hmr.js',
|
|
42
|
+
'overlay-selection': 'overlay-selection.js',
|
|
43
|
+
'text-toolbar': 'text-toolbar.js',
|
|
44
|
+
'toolbar-styles': 'toolbar-styles.js',
|
|
45
|
+
'editor-toolbar': 'editor-toolbar.js',
|
|
46
|
+
'css-rules': 'css-rules.js',
|
|
47
|
+
'ast-modification': 'ast-modification.js',
|
|
48
|
+
'ast-service': 'ast-service.js',
|
|
49
|
+
'file-service': 'file-service.js',
|
|
50
|
+
'file-editor': 'file-editor.js',
|
|
51
|
+
'syntax-highlighter': 'syntax-highlighter.js',
|
|
52
|
+
'property-registry': 'property-registry.js',
|
|
53
|
+
'properties-panel': 'properties-panel.js',
|
|
54
|
+
'properties-panel-persist': 'properties-panel-persist.js',
|
|
55
|
+
'properties-panel-rows': 'properties-panel-rows.js',
|
|
56
|
+
'properties-panel-styles': 'properties-panel-styles.js',
|
|
57
|
+
'i18n-key-gen': 'i18n-key-gen.js',
|
|
58
|
+
'ai-chat-panel': 'ai-chat-panel.js',
|
|
59
|
+
'ai-project-panel': 'ai-project-panel.js',
|
|
60
|
+
'ai-markdown': 'ai-markdown.js',
|
|
28
61
|
};
|
|
29
62
|
function rewriteRelativeImports(code, modules) {
|
|
30
63
|
for (const name of Object.keys(modules)) {
|
|
@@ -32,14 +65,26 @@ export function virtualModulesPlugin(runtimeDir, editorDir) {
|
|
|
32
65
|
// Aliased modules use @lumenjs/name (resolved by Vite alias).
|
|
33
66
|
// Virtual modules use /@lumenjs/name (resolved by this plugin).
|
|
34
67
|
const prefix = aliasedModules.has(name) ? '@lumenjs' : '/@lumenjs';
|
|
35
|
-
|
|
68
|
+
const escaped = file.replace('.', '\\.');
|
|
69
|
+
// Rewrite `from './file.js'`
|
|
70
|
+
code = code.replace(new RegExp(`from\\s+['"]\\.\\/${escaped}['"]`, 'g'), `from '${prefix}/${name}'`);
|
|
71
|
+
// Rewrite side-effect `import './file.js'`
|
|
72
|
+
code = code.replace(new RegExp(`import\\s+['"]\\.\\/${escaped}['"]`, 'g'), `import '${prefix}/${name}'`);
|
|
36
73
|
}
|
|
37
74
|
return code;
|
|
38
75
|
}
|
|
76
|
+
let viteBase = '/';
|
|
39
77
|
return {
|
|
40
78
|
name: 'lumenjs-virtual-modules',
|
|
41
79
|
enforce: 'pre',
|
|
80
|
+
configResolved(config) {
|
|
81
|
+
viteBase = config.base || '/';
|
|
82
|
+
},
|
|
42
83
|
resolveId(id) {
|
|
84
|
+
// Strip Vite base prefix if present (e.g. /__app_dev/{id}/@lumenjs/foo → /@lumenjs/foo)
|
|
85
|
+
if (viteBase !== '/' && id.startsWith(viteBase)) {
|
|
86
|
+
id = '/' + id.slice(viteBase.length);
|
|
87
|
+
}
|
|
43
88
|
const match = id.match(/^\/@lumenjs\/(.+)$/);
|
|
44
89
|
if (!match)
|
|
45
90
|
return;
|
|
@@ -55,8 +100,100 @@ export function virtualModulesPlugin(runtimeDir, editorDir) {
|
|
|
55
100
|
if (!id.startsWith('\0lumenjs:'))
|
|
56
101
|
return;
|
|
57
102
|
const name = id.slice('\0lumenjs:'.length);
|
|
103
|
+
if (name === 'hydrate-support') {
|
|
104
|
+
// Custom hydrate support that catches digest mismatch errors and falls
|
|
105
|
+
// back to CSR instead of leaving double-rendered content.
|
|
106
|
+
// The stock @lit-labs/ssr-client/lit-element-hydrate-support.js throws
|
|
107
|
+
// on digest mismatch, sets _$AG=false before throwing, so the next
|
|
108
|
+
// update() appends fresh render alongside stale SSR content.
|
|
109
|
+
return `
|
|
110
|
+
import { render } from 'lit-html';
|
|
111
|
+
import { hydrate } from '@lit-labs/ssr-client';
|
|
112
|
+
|
|
113
|
+
globalThis.litElementHydrateSupport = ({LitElement}) => {
|
|
114
|
+
const observedGet = Object.getOwnPropertyDescriptor(
|
|
115
|
+
Object.getPrototypeOf(LitElement), 'observedAttributes'
|
|
116
|
+
).get;
|
|
117
|
+
Object.defineProperty(LitElement, 'observedAttributes', {
|
|
118
|
+
get() { return [...observedGet.call(this), 'defer-hydration']; }
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
const origAttrChanged = LitElement.prototype.attributeChangedCallback;
|
|
122
|
+
LitElement.prototype.attributeChangedCallback = function(name, old, value) {
|
|
123
|
+
if (name === 'defer-hydration' && value === null) {
|
|
124
|
+
origConnected.call(this);
|
|
125
|
+
}
|
|
126
|
+
origAttrChanged.call(this, name, old, value);
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
const origConnected = LitElement.prototype.connectedCallback;
|
|
130
|
+
LitElement.prototype.connectedCallback = function() {
|
|
131
|
+
if (!this.hasAttribute('defer-hydration')) origConnected.call(this);
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
function adoptElementStyles(el) {
|
|
135
|
+
const styles = el.constructor.elementStyles;
|
|
136
|
+
if (styles?.length && el.renderRoot instanceof ShadowRoot) {
|
|
137
|
+
el.renderRoot.adoptedStyleSheets = styles.map(
|
|
138
|
+
s => s instanceof CSSStyleSheet ? s : s.styleSheet
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const origCreateRoot = LitElement.prototype.createRenderRoot;
|
|
144
|
+
LitElement.prototype.createRenderRoot = function() {
|
|
145
|
+
if (this.shadowRoot) {
|
|
146
|
+
this._$AG = true;
|
|
147
|
+
// Adopt styles that SSR declarative shadow roots don't include
|
|
148
|
+
adoptElementStyles(this);
|
|
149
|
+
return this.shadowRoot;
|
|
150
|
+
}
|
|
151
|
+
return origCreateRoot.call(this);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const superUpdate = Object.getPrototypeOf(LitElement.prototype).update;
|
|
155
|
+
LitElement.prototype.update = function(changedProps) {
|
|
156
|
+
const value = this.render();
|
|
157
|
+
superUpdate.call(this, changedProps);
|
|
158
|
+
if (this._$AG) {
|
|
159
|
+
this._$AG = false;
|
|
160
|
+
for (const attr of this.getAttributeNames()) {
|
|
161
|
+
if (attr.startsWith('hydrate-internals-')) {
|
|
162
|
+
this.removeAttribute(attr.slice(18));
|
|
163
|
+
this.removeAttribute(attr);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
hydrate(value, this.renderRoot, this.renderOptions);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
// Digest mismatch — re-render fresh but avoid visible flash
|
|
170
|
+
console.warn('[LumenJS] Hydration mismatch for <' + this.localName + '>, falling back to CSR');
|
|
171
|
+
const root = this.renderRoot;
|
|
172
|
+
// Preserve adopted styles so content is never unstyled
|
|
173
|
+
adoptElementStyles(this);
|
|
174
|
+
// Remove only non-style children to keep styles applied during re-render
|
|
175
|
+
const toRemove = [];
|
|
176
|
+
for (let c = root.firstChild; c; c = c.nextSibling) {
|
|
177
|
+
if (c.nodeName !== 'STYLE') toRemove.push(c);
|
|
178
|
+
}
|
|
179
|
+
toRemove.forEach(c => root.removeChild(c));
|
|
180
|
+
delete root._$litPart$;
|
|
181
|
+
render(value, root, this.renderOptions);
|
|
182
|
+
}
|
|
183
|
+
} else {
|
|
184
|
+
render(value, this.renderRoot, this.renderOptions);
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
};
|
|
188
|
+
`;
|
|
189
|
+
}
|
|
58
190
|
if (runtimeModules[name]) {
|
|
59
|
-
|
|
191
|
+
let code = fs.readFileSync(path.join(runtimeDir, runtimeModules[name]), 'utf-8');
|
|
192
|
+
// Prepend hydrate support to app-shell so all Lit imports share one module graph.
|
|
193
|
+
// Uses our custom hydrate-support virtual module (with CSR fallback) instead of stock.
|
|
194
|
+
if (name === 'app-shell') {
|
|
195
|
+
code = `import '/@lumenjs/hydrate-support';\n` + code;
|
|
196
|
+
}
|
|
60
197
|
return rewriteRelativeImports(code, runtimeModules);
|
|
61
198
|
}
|
|
62
199
|
if (editorModules[name]) {
|