glashjs 0.13.2 → 0.13.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/bin/glash.mjs +18 -2
- package/package.json +1 -1
- package/src/create.mjs +12 -5
- package/src/next/dynamic.mjs +16 -0
- package/src/next/headers.mjs +23 -0
- package/src/next/navigation.mjs +50 -0
- package/src/server/jsx.mjs +9 -2
package/bin/glash.mjs
CHANGED
|
@@ -42,6 +42,20 @@ function firstPositional(args = rest) {
|
|
|
42
42
|
return undefined;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// A compact terminal rendering of the glashjs favicon — the metallic glash mark
|
|
46
|
+
// (a rounded square). Shown in the dev/serve banner so the brand favicon is
|
|
47
|
+
// visible right in the CLI. ANSI-colored on a TTY; plain blocks otherwise.
|
|
48
|
+
function glashMark() {
|
|
49
|
+
const tty = process.stdout.isTTY;
|
|
50
|
+
const hi = (s) => (tty ? `\x1b[97m${s}\x1b[0m` : s); // bright white highlight
|
|
51
|
+
const mid = (s) => (tty ? `\x1b[38;5;250m${s}\x1b[0m` : s); // light grey shading
|
|
52
|
+
return [
|
|
53
|
+
' ' + hi('▟██████▙'),
|
|
54
|
+
' ' + hi('██') + mid('████') + hi('██'),
|
|
55
|
+
' ' + hi('▜██████▛'),
|
|
56
|
+
];
|
|
57
|
+
}
|
|
58
|
+
|
|
45
59
|
// LAN IPv4 addresses, so the dev server prints a Network URL you can open from
|
|
46
60
|
// your phone or another device on the same network.
|
|
47
61
|
function lanAddresses() {
|
|
@@ -62,7 +76,9 @@ async function serve(dev) {
|
|
|
62
76
|
const port = await listenOnAvailablePort(listen, preferredPort);
|
|
63
77
|
const pages = routes.filter((r) => !r.isApi).length;
|
|
64
78
|
const apis = routes.filter((r) => r.isApi).length;
|
|
65
|
-
console.log(
|
|
79
|
+
console.log('');
|
|
80
|
+
for (const line of glashMark()) console.log(line);
|
|
81
|
+
console.log(` glashjs ${dev ? 'dev' : 'serve'} — "${cfg.name}"`);
|
|
66
82
|
if (port !== preferredPort) console.log(` Port ${preferredPort} is in use; using ${port} instead.`);
|
|
67
83
|
console.log(` ${pages} page route(s), ${apis} api route(s)`);
|
|
68
84
|
routes.forEach((r) => console.log(` ${r.isApi ? 'api ' : 'page'} ${r.pattern}`));
|
|
@@ -157,7 +173,7 @@ async function main() {
|
|
|
157
173
|
console.log('glashjs ' + VERSION);
|
|
158
174
|
break;
|
|
159
175
|
default:
|
|
160
|
-
console.log(`glashjs — fast, offline-capable,
|
|
176
|
+
console.log(`glashjs — fast, offline-capable, secure-by-default sites
|
|
161
177
|
|
|
162
178
|
Usage: (run as "glashjs <cmd>"; "glash <cmd>" also works unless the glashdb deploy CLI owns that name)
|
|
163
179
|
glashjs run dev Alias for glashjs dev (also: glash run dev)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "glashjs",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.4",
|
|
4
4
|
"description": "glashjs — The Postgres-native full-stack framework for builders who want to ship without DevOps. Framework, hosting, database, auth, and deploy in one GlashDB-native runtime.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/src/create.mjs
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import { spawn } from 'node:child_process';
|
|
6
6
|
import { randomBytes } from 'node:crypto';
|
|
7
|
-
import { promises as fs } from 'node:fs';
|
|
7
|
+
import { promises as fs, readFileSync } from 'node:fs';
|
|
8
8
|
import { createInterface } from 'node:readline/promises';
|
|
9
9
|
import path from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
10
11
|
import { generateTypedRoutes } from './typed-routes.mjs';
|
|
11
12
|
|
|
12
13
|
const CSS_CHOICES = new Set(['tailwind', 'plain', 'none']);
|
|
@@ -475,12 +476,18 @@ function tailwindInput() {
|
|
|
475
476
|
}
|
|
476
477
|
|
|
477
478
|
function faviconSvg() {
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
479
|
+
// Use the bundled glash mark (the official metallic glash favicon) so a new
|
|
480
|
+
// app ships the real brand favicon, not a placeholder. Single source of truth:
|
|
481
|
+
// glashjs/templates/favicon.svg.
|
|
482
|
+
try {
|
|
483
|
+
return readFileSync(fileURLToPath(new URL('../templates/favicon.svg', import.meta.url)), 'utf8');
|
|
484
|
+
} catch {
|
|
485
|
+
return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64">
|
|
486
|
+
<rect width="64" height="64" rx="14" fill="#0a0a0a"/>
|
|
487
|
+
<rect x="13" y="13" width="38" height="38" rx="11" fill="#d4d4d4"/>
|
|
482
488
|
</svg>
|
|
483
489
|
`;
|
|
490
|
+
}
|
|
484
491
|
}
|
|
485
492
|
|
|
486
493
|
function gitignore() {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Minimal next/dynamic compatibility for migrated client components.
|
|
2
|
+
import { h } from 'preact';
|
|
3
|
+
import { lazy, Suspense } from 'preact/compat';
|
|
4
|
+
|
|
5
|
+
export default function dynamic(loader, options = {}) {
|
|
6
|
+
const Lazy = lazy(async () => {
|
|
7
|
+
const mod = await loader();
|
|
8
|
+
return { default: mod.default || mod };
|
|
9
|
+
});
|
|
10
|
+
const Loading = options.loading;
|
|
11
|
+
|
|
12
|
+
return function DynamicComponent(props) {
|
|
13
|
+
const fallback = Loading ? h(Loading, props) : null;
|
|
14
|
+
return h(Suspense, { fallback }, h(Lazy, props));
|
|
15
|
+
};
|
|
16
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Minimal next/headers compatibility for migrated API helpers.
|
|
2
|
+
// Glash handlers should prefer ctx.headers/cookies, but these no-op shims keep
|
|
3
|
+
// shared Next-era utilities importable during migration.
|
|
4
|
+
|
|
5
|
+
function emptyCookieStore() {
|
|
6
|
+
return {
|
|
7
|
+
get: () => undefined,
|
|
8
|
+
getAll: () => [],
|
|
9
|
+
has: () => false,
|
|
10
|
+
set: () => {},
|
|
11
|
+
delete: () => {},
|
|
12
|
+
clear: () => {},
|
|
13
|
+
toString: () => '',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function cookies() {
|
|
18
|
+
return emptyCookieStore();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function headers() {
|
|
22
|
+
return new Headers();
|
|
23
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Minimal Next navigation compatibility for migrated Glash routes.
|
|
2
|
+
// These browser-first shims keep common client components running without
|
|
3
|
+
// dragging the Next runtime into the Glash server bundle.
|
|
4
|
+
|
|
5
|
+
function hrefOf(href) {
|
|
6
|
+
if (typeof href === 'string') return href;
|
|
7
|
+
if (href && typeof href === 'object') {
|
|
8
|
+
const pathname = href.pathname || '';
|
|
9
|
+
const query = href.query ? `?${new URLSearchParams(href.query).toString()}` : '';
|
|
10
|
+
const hash = href.hash || '';
|
|
11
|
+
return `${pathname}${query}${hash}`;
|
|
12
|
+
}
|
|
13
|
+
return '/';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function useRouter() {
|
|
17
|
+
return {
|
|
18
|
+
push: (href) => { if (typeof window !== 'undefined') window.location.assign(hrefOf(href)); },
|
|
19
|
+
replace: (href) => { if (typeof window !== 'undefined') window.location.replace(hrefOf(href)); },
|
|
20
|
+
refresh: () => { if (typeof window !== 'undefined') window.location.reload(); },
|
|
21
|
+
back: () => { if (typeof window !== 'undefined') window.history.back(); },
|
|
22
|
+
forward: () => { if (typeof window !== 'undefined') window.history.forward(); },
|
|
23
|
+
prefetch: () => Promise.resolve(),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function usePathname() {
|
|
28
|
+
return typeof window === 'undefined' ? '/' : window.location.pathname;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function useSearchParams() {
|
|
32
|
+
return new URLSearchParams(typeof window === 'undefined' ? '' : window.location.search);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function useParams() {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function redirect(href) {
|
|
40
|
+
if (typeof window !== 'undefined') window.location.replace(hrefOf(href));
|
|
41
|
+
const error = new Error(`NEXT_REDIRECT:${hrefOf(href)}`);
|
|
42
|
+
error.digest = `NEXT_REDIRECT;replace;${hrefOf(href)};307;`;
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function notFound() {
|
|
47
|
+
const error = new Error('NEXT_NOT_FOUND');
|
|
48
|
+
error.status = 404;
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
package/src/server/jsx.mjs
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { promises as fs, existsSync } from 'node:fs';
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { createHash } from 'node:crypto';
|
|
19
|
-
import { pathToFileURL } from 'node:url';
|
|
19
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
20
20
|
|
|
21
21
|
const MISSING = 'JSX/TSX routes need the optional peers: npm i esbuild preact preact-render-to-string';
|
|
22
22
|
|
|
@@ -38,13 +38,20 @@ async function preactRuntime() {
|
|
|
38
38
|
return { h: _h, renderToString: _renderToString };
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
const pkgRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
42
|
+
|
|
43
|
+
// Make migrated React/Next components compile + run under Preact/Glash.
|
|
42
44
|
const REACT_ALIAS = {
|
|
43
45
|
react: 'preact/compat',
|
|
44
46
|
'react-dom': 'preact/compat',
|
|
45
47
|
'react-dom/client': 'preact/compat',
|
|
46
48
|
'react/jsx-runtime': 'preact/jsx-runtime',
|
|
47
49
|
'react/jsx-dev-runtime': 'preact/jsx-runtime',
|
|
50
|
+
'next/link': path.join(pkgRoot, 'components/link.mjs'),
|
|
51
|
+
'next/image': path.join(pkgRoot, 'components/image.mjs'),
|
|
52
|
+
'next/navigation': path.join(pkgRoot, 'next/navigation.mjs'),
|
|
53
|
+
'next/dynamic': path.join(pkgRoot, 'next/dynamic.mjs'),
|
|
54
|
+
'next/headers': path.join(pkgRoot, 'next/headers.mjs'),
|
|
48
55
|
};
|
|
49
56
|
|
|
50
57
|
export function isComponentRoute(file) {
|