@nuraly/lumenjs 0.1.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.
Files changed (76) hide show
  1. package/README.md +297 -0
  2. package/dist/build/build.d.ts +5 -0
  3. package/dist/build/build.js +172 -0
  4. package/dist/build/error-page.d.ts +1 -0
  5. package/dist/build/error-page.js +74 -0
  6. package/dist/build/scan.d.ts +21 -0
  7. package/dist/build/scan.js +93 -0
  8. package/dist/build/serve-api.d.ts +3 -0
  9. package/dist/build/serve-api.js +56 -0
  10. package/dist/build/serve-loaders.d.ts +4 -0
  11. package/dist/build/serve-loaders.js +115 -0
  12. package/dist/build/serve-ssr.d.ts +7 -0
  13. package/dist/build/serve-ssr.js +121 -0
  14. package/dist/build/serve-static.d.ts +6 -0
  15. package/dist/build/serve-static.js +80 -0
  16. package/dist/build/serve.d.ts +5 -0
  17. package/dist/build/serve.js +79 -0
  18. package/dist/cli.d.ts +2 -0
  19. package/dist/cli.js +65 -0
  20. package/dist/dev-server/config.d.ts +25 -0
  21. package/dist/dev-server/config.js +55 -0
  22. package/dist/dev-server/index-html.d.ts +16 -0
  23. package/dist/dev-server/index-html.js +46 -0
  24. package/dist/dev-server/nuralyui-aliases.d.ts +16 -0
  25. package/dist/dev-server/nuralyui-aliases.js +164 -0
  26. package/dist/dev-server/plugins/vite-plugin-api-routes.d.ts +23 -0
  27. package/dist/dev-server/plugins/vite-plugin-api-routes.js +250 -0
  28. package/dist/dev-server/plugins/vite-plugin-auto-import.d.ts +5 -0
  29. package/dist/dev-server/plugins/vite-plugin-auto-import.js +47 -0
  30. package/dist/dev-server/plugins/vite-plugin-lit-dedup.d.ts +5 -0
  31. package/dist/dev-server/plugins/vite-plugin-lit-dedup.js +62 -0
  32. package/dist/dev-server/plugins/vite-plugin-lit-hmr.d.ts +5 -0
  33. package/dist/dev-server/plugins/vite-plugin-lit-hmr.js +46 -0
  34. package/dist/dev-server/plugins/vite-plugin-loaders.d.ts +38 -0
  35. package/dist/dev-server/plugins/vite-plugin-loaders.js +320 -0
  36. package/dist/dev-server/plugins/vite-plugin-routes.d.ts +21 -0
  37. package/dist/dev-server/plugins/vite-plugin-routes.js +157 -0
  38. package/dist/dev-server/plugins/vite-plugin-source-annotator.d.ts +5 -0
  39. package/dist/dev-server/plugins/vite-plugin-source-annotator.js +39 -0
  40. package/dist/dev-server/plugins/vite-plugin-virtual-modules.d.ts +5 -0
  41. package/dist/dev-server/plugins/vite-plugin-virtual-modules.js +38 -0
  42. package/dist/dev-server/server.d.ts +23 -0
  43. package/dist/dev-server/server.js +155 -0
  44. package/dist/dev-server/ssr-render.d.ts +20 -0
  45. package/dist/dev-server/ssr-render.js +170 -0
  46. package/dist/editor/click-select.d.ts +1 -0
  47. package/dist/editor/click-select.js +46 -0
  48. package/dist/editor/editor-bridge.d.ts +17 -0
  49. package/dist/editor/editor-bridge.js +101 -0
  50. package/dist/editor/element-annotator.d.ts +33 -0
  51. package/dist/editor/element-annotator.js +83 -0
  52. package/dist/editor/hover-detect.d.ts +1 -0
  53. package/dist/editor/hover-detect.js +36 -0
  54. package/dist/editor/inline-text-edit.d.ts +1 -0
  55. package/dist/editor/inline-text-edit.js +114 -0
  56. package/dist/integrations/add.d.ts +1 -0
  57. package/dist/integrations/add.js +89 -0
  58. package/dist/runtime/app-shell.d.ts +1 -0
  59. package/dist/runtime/app-shell.js +22 -0
  60. package/dist/runtime/response.d.ts +15 -0
  61. package/dist/runtime/response.js +13 -0
  62. package/dist/runtime/router-data.d.ts +3 -0
  63. package/dist/runtime/router-data.js +40 -0
  64. package/dist/runtime/router-hydration.d.ts +10 -0
  65. package/dist/runtime/router-hydration.js +68 -0
  66. package/dist/runtime/router.d.ts +35 -0
  67. package/dist/runtime/router.js +202 -0
  68. package/dist/shared/dom-shims.d.ts +5 -0
  69. package/dist/shared/dom-shims.js +63 -0
  70. package/dist/shared/route-matching.d.ts +6 -0
  71. package/dist/shared/route-matching.js +44 -0
  72. package/dist/shared/types.d.ts +16 -0
  73. package/dist/shared/types.js +1 -0
  74. package/dist/shared/utils.d.ts +42 -0
  75. package/dist/shared/utils.js +109 -0
  76. package/package.json +53 -0
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Install DOM shims needed for SSR rendering of Lit/NuralyUI components.
3
+ * Consolidates the various partial shim implementations across the codebase.
4
+ */
5
+ export function installDomShims() {
6
+ const g = globalThis;
7
+ const noop = () => null;
8
+ if (!g.HTMLElement) {
9
+ g.HTMLElement = class HTMLElement {
10
+ };
11
+ }
12
+ if (!g.customElements) {
13
+ const registry = new Map();
14
+ g.customElements = {
15
+ get: (name) => registry.get(name),
16
+ define: (name, ctor) => registry.set(name, ctor),
17
+ };
18
+ }
19
+ if (!g.document) {
20
+ g.document = {
21
+ createTreeWalker: () => ({ nextNode: () => null }),
22
+ body: {},
23
+ querySelectorAll: () => [],
24
+ querySelector: () => null,
25
+ addEventListener: noop,
26
+ documentElement: { getAttribute: noop, setAttribute: noop, removeAttribute: noop, closest: noop },
27
+ createComment: (text) => ({ textContent: text }),
28
+ createTextNode: (text) => ({ textContent: text }),
29
+ };
30
+ }
31
+ // Patch missing document properties (SSR DOM shim may not include all of them)
32
+ if (g.document && !g.document.documentElement) {
33
+ g.document.documentElement = { getAttribute: noop, setAttribute: noop, removeAttribute: noop, closest: noop };
34
+ }
35
+ if (g.document?.documentElement && !g.document.documentElement.getAttribute) {
36
+ g.document.documentElement.getAttribute = noop;
37
+ }
38
+ if (!g.window) {
39
+ g.window = g;
40
+ }
41
+ if (!g.window.matchMedia) {
42
+ g.window.matchMedia = () => ({ matches: false, addEventListener: noop, removeEventListener: noop });
43
+ }
44
+ if (!g.CSSStyleSheet) {
45
+ g.CSSStyleSheet = class CSSStyleSheet {
46
+ };
47
+ }
48
+ if (!g.MutationObserver) {
49
+ g.MutationObserver = class MutationObserver {
50
+ observe() { }
51
+ disconnect() { }
52
+ };
53
+ }
54
+ if (g.HTMLElement && !g.HTMLElement.prototype.closest) {
55
+ g.HTMLElement.prototype.closest = noop;
56
+ }
57
+ if (g.HTMLElement && !g.HTMLElement.prototype.querySelector) {
58
+ g.HTMLElement.prototype.querySelector = noop;
59
+ }
60
+ if (g.HTMLElement && !g.HTMLElement.prototype.querySelectorAll) {
61
+ g.HTMLElement.prototype.querySelectorAll = () => [];
62
+ }
63
+ }
@@ -0,0 +1,6 @@
1
+ import type { ManifestRoute } from './types.js';
2
+ export interface MatchResult {
3
+ route: ManifestRoute;
4
+ params: Record<string, string>;
5
+ }
6
+ export declare function matchRoute(routes: ManifestRoute[], pathname: string): MatchResult | null;
@@ -0,0 +1,44 @@
1
+ export function matchRoute(routes, pathname) {
2
+ const urlSegments = pathname.replace(/^\//, '').split('/').filter(Boolean);
3
+ for (const route of routes) {
4
+ const routeSegments = route.path.replace(/^\//, '').split('/').filter(Boolean);
5
+ // Handle root route
6
+ if (route.path === '/' && (pathname === '/' || pathname === '')) {
7
+ return { route, params: {} };
8
+ }
9
+ const params = {};
10
+ let match = true;
11
+ for (let i = 0; i < routeSegments.length; i++) {
12
+ const seg = routeSegments[i];
13
+ if (seg.startsWith(':...')) {
14
+ // Catch-all: capture remaining URL segments
15
+ if (i < urlSegments.length) {
16
+ params[seg.slice(4)] = urlSegments.slice(i).join('/');
17
+ }
18
+ else {
19
+ match = false;
20
+ }
21
+ break;
22
+ }
23
+ else if (seg.startsWith(':')) {
24
+ if (i >= urlSegments.length) {
25
+ match = false;
26
+ break;
27
+ }
28
+ params[seg.slice(1)] = urlSegments[i];
29
+ }
30
+ else if (i >= urlSegments.length || seg !== urlSegments[i]) {
31
+ match = false;
32
+ break;
33
+ }
34
+ // For non-catch-all routes, lengths must match
35
+ if (i === routeSegments.length - 1 && routeSegments.length !== urlSegments.length) {
36
+ match = false;
37
+ }
38
+ }
39
+ if (match) {
40
+ return { route, params };
41
+ }
42
+ }
43
+ return null;
44
+ }
@@ -0,0 +1,16 @@
1
+ export interface ManifestLayout {
2
+ dir: string;
3
+ module: string;
4
+ hasLoader: boolean;
5
+ }
6
+ export interface ManifestRoute {
7
+ path: string;
8
+ module: string;
9
+ hasLoader: boolean;
10
+ layouts?: string[];
11
+ }
12
+ export interface BuildManifest {
13
+ routes: ManifestRoute[];
14
+ apiRoutes: ManifestRoute[];
15
+ layouts: ManifestLayout[];
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Strip the outer Lit SSR template markers from rendered HTML.
3
+ * Lit SSR wraps every template render in <!--lit-part HASH-->...<!--/lit-part-->.
4
+ * When inserting SSR HTML as light DOM (slot content), these markers must be
5
+ * removed so the parent element's hydration doesn't see a TemplateResult part.
6
+ */
7
+ export declare function stripOuterLitMarkers(html: string): string;
8
+ /**
9
+ * Convert a relative directory path within pages/ to a layout tag name.
10
+ * '' → 'layout-root'
11
+ * 'dashboard' → 'layout-dashboard'
12
+ * 'app/[id]' → 'layout-app-id'
13
+ */
14
+ export declare function dirToLayoutTagName(dir: string): string;
15
+ /**
16
+ * Find the custom element tag name from a page module.
17
+ * Pages use @customElement('page-xxx') which registers the element.
18
+ */
19
+ export declare function findTagName(mod: Record<string, any>): string | null;
20
+ /**
21
+ * Check if a redirect response was returned from a loader.
22
+ */
23
+ export declare function isRedirectResponse(value: any): value is {
24
+ location: string;
25
+ status?: number;
26
+ };
27
+ /**
28
+ * Read and parse the body of an HTTP request.
29
+ */
30
+ export declare function readBody(req: any): Promise<any>;
31
+ /**
32
+ * Escape HTML special characters for safe embedding.
33
+ */
34
+ export declare function escapeHtml(text: string): string;
35
+ /**
36
+ * Check if a page/layout file exports a loader() function.
37
+ */
38
+ export declare function fileHasLoader(filePath: string): boolean;
39
+ /**
40
+ * Convert a file path (relative to pages/) to a route path.
41
+ */
42
+ export declare function filePathToRoute(filePath: string): string;
@@ -0,0 +1,109 @@
1
+ import fs from 'fs';
2
+ /**
3
+ * Strip the outer Lit SSR template markers from rendered HTML.
4
+ * Lit SSR wraps every template render in <!--lit-part HASH-->...<!--/lit-part-->.
5
+ * When inserting SSR HTML as light DOM (slot content), these markers must be
6
+ * removed so the parent element's hydration doesn't see a TemplateResult part.
7
+ */
8
+ export function stripOuterLitMarkers(html) {
9
+ let result = html.replace(/^(<!--lit-part [^>]*-->)(\s*<!--lit-node \d+-->)?/, '');
10
+ result = result.replace(/<!--\/lit-part-->\s*$/, '');
11
+ return result;
12
+ }
13
+ /**
14
+ * Convert a relative directory path within pages/ to a layout tag name.
15
+ * '' → 'layout-root'
16
+ * 'dashboard' → 'layout-dashboard'
17
+ * 'app/[id]' → 'layout-app-id'
18
+ */
19
+ export function dirToLayoutTagName(dir) {
20
+ if (!dir)
21
+ return 'layout-root';
22
+ const name = dir
23
+ .replace(/\\/g, '-')
24
+ .replace(/\//g, '-')
25
+ .replace(/\[\.\.\.([^\]]+)\]/g, '$1')
26
+ .replace(/\[([^\]]+)\]/g, '$1')
27
+ .toLowerCase();
28
+ return `layout-${name}`;
29
+ }
30
+ /**
31
+ * Find the custom element tag name from a page module.
32
+ * Pages use @customElement('page-xxx') which registers the element.
33
+ */
34
+ export function findTagName(mod) {
35
+ for (const key of Object.keys(mod)) {
36
+ const val = mod[key];
37
+ if (typeof val === 'function' && val.prototype) {
38
+ if (val.is)
39
+ return val.is;
40
+ if (val.elementProperties || val.properties) {
41
+ const className = val.name || key;
42
+ const tag = className
43
+ .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
44
+ .toLowerCase();
45
+ if (tag.includes('-'))
46
+ return tag;
47
+ }
48
+ }
49
+ }
50
+ return null;
51
+ }
52
+ /**
53
+ * Check if a redirect response was returned from a loader.
54
+ */
55
+ export function isRedirectResponse(value) {
56
+ return value && typeof value === 'object' && typeof value.location === 'string' && value.__nk_redirect === true;
57
+ }
58
+ /**
59
+ * Read and parse the body of an HTTP request.
60
+ */
61
+ export function readBody(req) {
62
+ return new Promise((resolve, reject) => {
63
+ let data = '';
64
+ req.on('data', (chunk) => { data += chunk.toString(); });
65
+ req.on('end', () => {
66
+ if (!data)
67
+ return resolve(undefined);
68
+ try {
69
+ resolve(JSON.parse(data));
70
+ }
71
+ catch {
72
+ resolve(data);
73
+ }
74
+ });
75
+ req.on('error', reject);
76
+ });
77
+ }
78
+ /**
79
+ * Escape HTML special characters for safe embedding.
80
+ */
81
+ export function escapeHtml(text) {
82
+ return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
83
+ }
84
+ /**
85
+ * Check if a page/layout file exports a loader() function.
86
+ */
87
+ export function fileHasLoader(filePath) {
88
+ try {
89
+ const content = fs.readFileSync(filePath, 'utf-8');
90
+ return /export\s+(async\s+)?function\s+loader\s*\(/.test(content);
91
+ }
92
+ catch {
93
+ return false;
94
+ }
95
+ }
96
+ /**
97
+ * Convert a file path (relative to pages/) to a route path.
98
+ */
99
+ export function filePathToRoute(filePath) {
100
+ let route = filePath
101
+ .replace(/\.(ts|js)$/, '')
102
+ .replace(/\\/g, '/')
103
+ .replace(/\[\.\.\.([^\]]+)\]/g, ':...$1')
104
+ .replace(/\[([^\]]+)\]/g, ':$1');
105
+ if (route === 'index' || route.endsWith('/index')) {
106
+ route = route.slice(0, -5).replace(/\/$/, '') || '/';
107
+ }
108
+ return route.startsWith('/') ? route : '/' + route;
109
+ }
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@nuraly/lumenjs",
3
+ "version": "0.1.0",
4
+ "description": "Full-stack Lit web component framework with file-based routing, server loaders, SSR, and API routes",
5
+ "type": "module",
6
+ "main": "dist/cli.js",
7
+ "bin": {
8
+ "lumenjs": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "dev": "tsc --watch",
17
+ "prepublishOnly": "tsc"
18
+ },
19
+ "keywords": [
20
+ "lit",
21
+ "web-components",
22
+ "ssr",
23
+ "vite",
24
+ "framework",
25
+ "file-based-routing",
26
+ "loaders",
27
+ "api-routes",
28
+ "hydration"
29
+ ],
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "https://github.com/Nuralyio/lumenjs.git"
33
+ },
34
+ "homepage": "https://github.com/Nuralyio/lumenjs",
35
+ "bugs": {
36
+ "url": "https://github.com/Nuralyio/lumenjs/issues"
37
+ },
38
+ "author": "labidiaymen <labidi@aymen.co>",
39
+ "license": "MIT",
40
+ "dependencies": {
41
+ "vite": "^5.4.0",
42
+ "lit": "^3.1.0",
43
+ "@lit-labs/ssr": "^3.2.0",
44
+ "glob": "^10.3.0"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "^5.4.5",
48
+ "@types/node": "^20.14.2"
49
+ },
50
+ "engines": {
51
+ "node": ">=18"
52
+ }
53
+ }