@jxsuite/site 2.4.0 → 2.5.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/package.json +3 -3
- package/src/compose.ts +19 -8
- package/src/paths.ts +2 -2
- package/src/serve.ts +13 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxsuite/site",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.5.0",
|
|
4
4
|
"description": "What a Jx project's page at a route consists of — routing, layout, context, head and the document that boots it",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"test:coverage": "bun test --isolate --coverage"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@jxsuite/runtime": "^6.2.
|
|
36
|
-
"@jxsuite/schema": "^5.
|
|
35
|
+
"@jxsuite/runtime": "^6.2.1",
|
|
36
|
+
"@jxsuite/schema": "^5.4.0"
|
|
37
37
|
}
|
|
38
38
|
}
|
package/src/compose.ts
CHANGED
|
@@ -32,7 +32,7 @@ import { isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
|
32
32
|
import { localeDirection, localeOfRoute, resolveI18n } from "@jxsuite/schema/locale";
|
|
33
33
|
import { isNativeDocumentPath, parseNativeDocument } from "@jxsuite/schema/jxn";
|
|
34
34
|
import type { JxDocument, JxElement, JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
|
|
35
|
-
import {
|
|
35
|
+
import { PROJECT_FILE_NAMES, parseProjectSource } from "@jxsuite/schema/project-file";
|
|
36
36
|
|
|
37
37
|
/** Everything the composer settled about a page before a renderer sees it. */
|
|
38
38
|
export interface ComposedPage {
|
|
@@ -95,17 +95,28 @@ async function readDocument(io: SiteIO, path: string): Promise<JxDocument | null
|
|
|
95
95
|
return io.parse ? ((await io.parse(path, text)) ?? null) : null;
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
-
/**
|
|
98
|
+
/**
|
|
99
|
+
* The project's configuration, or an empty one when it has none this host can read.
|
|
100
|
+
*
|
|
101
|
+
* Both names are read, because a host that can only fetch has no other way to know which one a
|
|
102
|
+
* project has. A project with both has neither, as it has everywhere else (`site-architecture.md`
|
|
103
|
+
* §2.3).
|
|
104
|
+
*/
|
|
99
105
|
export async function readProjectConfig(io: SiteIO): Promise<ProjectConfig> {
|
|
100
|
-
const
|
|
101
|
-
|
|
106
|
+
const texts = await Promise.all(PROJECT_FILE_NAMES.map((name) => io.read(name)));
|
|
107
|
+
const found = PROJECT_FILE_NAMES.flatMap((name, index) => {
|
|
108
|
+
const text = texts[index];
|
|
109
|
+
return text === null || text === undefined ? [] : [{ name, text }];
|
|
110
|
+
});
|
|
111
|
+
const [only] = found;
|
|
112
|
+
if (!only || found.length > 1) {
|
|
102
113
|
return {};
|
|
103
114
|
}
|
|
104
115
|
try {
|
|
105
|
-
/*
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return
|
|
116
|
+
/* Only the syntax is checked here, deliberately: every ProjectConfig member is optional, and
|
|
117
|
+
shape checking is `validateProject`'s job against the per-project schema, which `jx build`
|
|
118
|
+
runs and a live preview of a half-edited config must not refuse over. */
|
|
119
|
+
return parseProjectSource(only.text, only.name);
|
|
109
120
|
} catch {
|
|
110
121
|
/* A project whose config does not parse still has pages, and refusing to render any of them
|
|
111
122
|
would hide the one error the author can act on behind a blank origin. */
|
package/src/paths.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PROJECT_FILE_NAMES } from "@jxsuite/schema/project-file";
|
|
2
2
|
/**
|
|
3
3
|
* What a site origin serves, and — the part that matters — what it refuses.
|
|
4
4
|
*
|
|
@@ -42,7 +42,7 @@ export const SERVABLE_ROOTS: readonly string[] = [
|
|
|
42
42
|
* them twice is nothing.
|
|
43
43
|
*/
|
|
44
44
|
export const NEVER_SERVABLE: ReadonlySet<string> = new Set([
|
|
45
|
-
|
|
45
|
+
...PROJECT_FILE_NAMES,
|
|
46
46
|
"package.json",
|
|
47
47
|
"package-lock.json",
|
|
48
48
|
"bun.lock",
|
package/src/serve.ts
CHANGED
|
@@ -149,15 +149,24 @@ export async function serveSite(
|
|
|
149
149
|
*
|
|
150
150
|
* The runtime fetches a `$ref` and calls `res.json()`, on any origin, so answering JSON here keeps
|
|
151
151
|
* a KDL parser out of every page. A file that does not read answers 422 with the reason, because
|
|
152
|
-
* the runtime reports a failed fetch by its status and this one is the author's to fix.
|
|
152
|
+
* the runtime reports a failed fetch by its status and this one is the author's to fix. The dev
|
|
153
|
+
* server's canvas origin answers the same way, with its own headers.
|
|
154
|
+
*
|
|
155
|
+
* @param bytes The file.
|
|
156
|
+
* @param path Its path, which names it in an error.
|
|
157
|
+
* @param headers The headers for a response of a media type.
|
|
153
158
|
*/
|
|
154
|
-
function jxnAsJson(
|
|
159
|
+
export function jxnAsJson(
|
|
160
|
+
bytes: Uint8Array<ArrayBuffer>,
|
|
161
|
+
path: string,
|
|
162
|
+
headers: (type: string) => HeadersInit = siteHeaders,
|
|
163
|
+
): Response {
|
|
155
164
|
try {
|
|
156
165
|
const document = parseJxn(new TextDecoder().decode(bytes), path);
|
|
157
|
-
return Response.json(document, { headers:
|
|
166
|
+
return Response.json(document, { headers: headers("application/json;charset=utf-8") });
|
|
158
167
|
} catch (error) {
|
|
159
168
|
return new Response(errorMessage(error), {
|
|
160
|
-
headers:
|
|
169
|
+
headers: headers("text/plain;charset=utf-8"),
|
|
161
170
|
status: 422,
|
|
162
171
|
});
|
|
163
172
|
}
|