@askrjs/vite 0.0.6 → 0.0.8
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 +3 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -2
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +2 -2
- package/dist/server.js +110 -3
- package/dist/server.js.map +1 -1
- package/dist/virtual-askr-server.d.ts +6 -0
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# @askrjs/vite
|
|
2
2
|
|
|
3
|
+
[](https://github.com/askrjs/askr-vite/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/@askrjs/vite)
|
|
5
|
+
|
|
3
6
|
Vite plugin for Askr JSX and template transforms.
|
|
4
7
|
|
|
5
8
|
`@askrjs/vite` is the build-time glue that lets Askr projects use the
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
//#region src/jsx-plugin.d.ts
|
|
2
3
|
interface AskrVitePluginOptions {
|
|
3
4
|
transformJsx?: boolean;
|
|
@@ -16,5 +17,4 @@ interface AskrVitePlugin {
|
|
|
16
17
|
}
|
|
17
18
|
declare function askrVitePlugin(options?: AskrVitePluginOptions): AskrVitePlugin;
|
|
18
19
|
//#endregion
|
|
19
|
-
export { type AskrVitePlugin, type AskrVitePluginOptions, askrVitePlugin as askr, askrVitePlugin, askrVitePlugin as default };
|
|
20
|
-
//# sourceMappingURL=index.d.ts.map
|
|
20
|
+
export { type AskrVitePlugin, type AskrVitePluginOptions, askrVitePlugin as askr, askrVitePlugin, askrVitePlugin as default };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import { transformWithOxc } from "vite";
|
|
3
4
|
//#region src/template-optimizer.ts
|
|
@@ -94,5 +95,3 @@ function askrVitePlugin(options = {}) {
|
|
|
94
95
|
}
|
|
95
96
|
//#endregion
|
|
96
97
|
export { askrVitePlugin as askr, askrVitePlugin, askrVitePlugin as default };
|
|
97
|
-
|
|
98
|
-
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/template-optimizer.ts","../src/jsx-plugin.ts"],"sourcesContent":["export function optimizeTemplateOutput(code: string): string {\n const hoists = new Map<string, string>();\n let hoistIndex = 0;\n const optimized = code.replace(\n /\\b(class|className|style):\\s*(\"([^\"\\\\]|\\\\.)*\")/g,\n (fullMatch: string, key: string, literal: string) => {\n const cacheKey = `${key}:${literal}`;\n let identifier = hoists.get(cacheKey);\n if (!identifier) {\n if (code.split(fullMatch).length - 1 < 2) return fullMatch;\n identifier = `__askrStaticLiteral${hoistIndex++}`;\n hoists.set(cacheKey, identifier);\n }\n return `${key}: ${identifier}`;\n },\n );\n if (hoists.size === 0) return code;\n const declarations = Array.from(hoists.entries()).map(([cacheKey, name]) => {\n const literal = cacheKey.slice(cacheKey.indexOf(\":\") + 1);\n return `const ${name} = ${literal};`;\n });\n return `${declarations.join(\"\\n\")}\\n${optimized}`;\n}\n","import { createRequire } from \"node:module\";\nimport { transformWithOxc, type Plugin } from \"vite\";\nimport { optimizeTemplateOutput } from \"./template-optimizer\";\n\nconst require = createRequire(import.meta.url);\n\nexport interface AskrVitePluginOptions {\n transformJsx?: boolean;\n optimizeTemplates?: boolean;\n ssrPrecompile?: boolean;\n}\n\n/**\n * Portable public plugin identity.\n *\n * Vite-compatible runners only require the plugin name. Keeping versioned Vite\n * hook types out of the installed declaration prevents consumers such as\n * vite-plus from comparing two distinct copies of Vite's recursive Plugin type.\n */\nexport interface AskrVitePlugin {\n readonly name: \"askr:vite\";\n}\n\nexport function askrVitePlugin(options: AskrVitePluginOptions = {}): AskrVitePlugin {\n const shouldTransform = options.transformJsx ?? true;\n const shouldOptimizeTemplates = options.optimizeTemplates ?? false;\n const plugin = {\n name: \"askr:vite\" as const,\n enforce: \"pre\",\n config() {\n let askrEntry: string | null = null;\n try {\n askrEntry = require.resolve(\"@askrjs/askr\");\n } catch {\n askrEntry = null;\n }\n return {\n define: {\n __ASKR_OPTIMIZE_TEMPLATES__: JSON.stringify(shouldOptimizeTemplates),\n },\n resolve: {\n alias: askrEntry ? [{ find: \"@askrjs/askr\", replacement: askrEntry }] : [],\n dedupe: [\"@askrjs/askr\"],\n },\n optimizeDeps: {\n include: [\n \"@askrjs/askr\",\n \"@askrjs/askr/router\",\n \"@askrjs/askr/boot\",\n \"@askrjs/askr/control\",\n \"@askrjs/askr/fx\",\n \"@askrjs/askr/resources\",\n \"@askrjs/askr/data\",\n \"@askrjs/askr/foundations\",\n \"@askrjs/askr/foundations/icon\",\n \"@askrjs/askr/foundations/interactions\",\n \"@askrjs/askr/foundations/state\",\n \"@askrjs/askr/foundations/structures\",\n \"@askrjs/askr/foundations/utilities\",\n \"@askrjs/askr/jsx-runtime\",\n \"@askrjs/askr/jsx-dev-runtime\",\n ],\n },\n oxc: {\n jsx: { runtime: \"automatic\", importSource: \"@askrjs/askr\" },\n jsxInject: \"import { jsx, jsxs, Fragment } from '@askrjs/askr/jsx-runtime';\",\n },\n };\n },\n async transform(code, id) {\n if (!shouldTransform || !/\\.(jsx|tsx)$/.test(id) || id.includes(\"node_modules\")) return null;\n try {\n const result = await transformWithOxc(code, id, {\n lang: id.endsWith(\".tsx\") ? \"tsx\" : \"jsx\",\n jsx: { runtime: \"automatic\", importSource: \"@askrjs/askr\" },\n sourcemap: true,\n });\n if (!result?.code) return null;\n return {\n code: shouldOptimizeTemplates ? optimizeTemplateOutput(result.code) : result.code,\n map: result.map,\n };\n } catch {\n return null;\n }\n },\n } satisfies Plugin;\n return plugin;\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/template-optimizer.ts","../src/jsx-plugin.ts"],"sourcesContent":["export function optimizeTemplateOutput(code: string): string {\n const hoists = new Map<string, string>();\n let hoistIndex = 0;\n const optimized = code.replace(\n /\\b(class|className|style):\\s*(\"([^\"\\\\]|\\\\.)*\")/g,\n (fullMatch: string, key: string, literal: string) => {\n const cacheKey = `${key}:${literal}`;\n let identifier = hoists.get(cacheKey);\n if (!identifier) {\n if (code.split(fullMatch).length - 1 < 2) return fullMatch;\n identifier = `__askrStaticLiteral${hoistIndex++}`;\n hoists.set(cacheKey, identifier);\n }\n return `${key}: ${identifier}`;\n },\n );\n if (hoists.size === 0) return code;\n const declarations = Array.from(hoists.entries()).map(([cacheKey, name]) => {\n const literal = cacheKey.slice(cacheKey.indexOf(\":\") + 1);\n return `const ${name} = ${literal};`;\n });\n return `${declarations.join(\"\\n\")}\\n${optimized}`;\n}\n","import { createRequire } from \"node:module\";\nimport { transformWithOxc, type Plugin } from \"vite\";\nimport { optimizeTemplateOutput } from \"./template-optimizer\";\n\nconst require = createRequire(import.meta.url);\n\nexport interface AskrVitePluginOptions {\n transformJsx?: boolean;\n optimizeTemplates?: boolean;\n ssrPrecompile?: boolean;\n}\n\n/**\n * Portable public plugin identity.\n *\n * Vite-compatible runners only require the plugin name. Keeping versioned Vite\n * hook types out of the installed declaration prevents consumers such as\n * vite-plus from comparing two distinct copies of Vite's recursive Plugin type.\n */\nexport interface AskrVitePlugin {\n readonly name: \"askr:vite\";\n}\n\nexport function askrVitePlugin(options: AskrVitePluginOptions = {}): AskrVitePlugin {\n const shouldTransform = options.transformJsx ?? true;\n const shouldOptimizeTemplates = options.optimizeTemplates ?? false;\n const plugin = {\n name: \"askr:vite\" as const,\n enforce: \"pre\",\n config() {\n let askrEntry: string | null = null;\n try {\n askrEntry = require.resolve(\"@askrjs/askr\");\n } catch {\n askrEntry = null;\n }\n return {\n define: {\n __ASKR_OPTIMIZE_TEMPLATES__: JSON.stringify(shouldOptimizeTemplates),\n },\n resolve: {\n alias: askrEntry ? [{ find: \"@askrjs/askr\", replacement: askrEntry }] : [],\n dedupe: [\"@askrjs/askr\"],\n },\n optimizeDeps: {\n include: [\n \"@askrjs/askr\",\n \"@askrjs/askr/router\",\n \"@askrjs/askr/boot\",\n \"@askrjs/askr/control\",\n \"@askrjs/askr/fx\",\n \"@askrjs/askr/resources\",\n \"@askrjs/askr/data\",\n \"@askrjs/askr/foundations\",\n \"@askrjs/askr/foundations/icon\",\n \"@askrjs/askr/foundations/interactions\",\n \"@askrjs/askr/foundations/state\",\n \"@askrjs/askr/foundations/structures\",\n \"@askrjs/askr/foundations/utilities\",\n \"@askrjs/askr/jsx-runtime\",\n \"@askrjs/askr/jsx-dev-runtime\",\n ],\n },\n oxc: {\n jsx: { runtime: \"automatic\", importSource: \"@askrjs/askr\" },\n jsxInject: \"import { jsx, jsxs, Fragment } from '@askrjs/askr/jsx-runtime';\",\n },\n };\n },\n async transform(code, id) {\n if (!shouldTransform || !/\\.(jsx|tsx)$/.test(id) || id.includes(\"node_modules\")) return null;\n try {\n const result = await transformWithOxc(code, id, {\n lang: id.endsWith(\".tsx\") ? \"tsx\" : \"jsx\",\n jsx: { runtime: \"automatic\", importSource: \"@askrjs/askr\" },\n sourcemap: true,\n });\n if (!result?.code) return null;\n return {\n code: shouldOptimizeTemplates ? optimizeTemplateOutput(result.code) : result.code,\n map: result.map,\n };\n } catch {\n return null;\n }\n },\n } satisfies Plugin;\n return plugin;\n}\n"],"mappings":";;;;AAAA,SAAgB,uBAAuB,MAAsB;CAC3D,MAAM,yBAAS,IAAI,IAAoB;CACvC,IAAI,aAAa;CACjB,MAAM,YAAY,KAAK,QACrB,oDACC,WAAmB,KAAa,YAAoB;EACnD,MAAM,WAAW,GAAG,IAAI,GAAG;EAC3B,IAAI,aAAa,OAAO,IAAI,QAAQ;EACpC,IAAI,CAAC,YAAY;GACf,IAAI,KAAK,MAAM,SAAS,CAAC,CAAC,SAAS,IAAI,GAAG,OAAO;GACjD,aAAa,sBAAsB;GACnC,OAAO,IAAI,UAAU,UAAU;EACjC;EACA,OAAO,GAAG,IAAI,IAAI;CACpB,CACF;CACA,IAAI,OAAO,SAAS,GAAG,OAAO;CAK9B,OAAO,GAJc,MAAM,KAAK,OAAO,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,UAAU;EAE1E,OAAO,SAAS,KAAK,KADL,SAAS,MAAM,SAAS,QAAQ,GAAG,IAAI,CACvB,EAAE;CACpC,CACqB,CAAC,CAAC,KAAK,IAAI,EAAE,IAAI;AACxC;;;AClBA,MAAM,UAAU,cAAc,OAAO,KAAK,GAAG;AAmB7C,SAAgB,eAAe,UAAiC,CAAC,GAAmB;CAClF,MAAM,kBAAkB,QAAQ,gBAAgB;CAChD,MAAM,0BAA0B,QAAQ,qBAAqB;CA8D7D,OAAO;EA5DL,MAAM;EACN,SAAS;EACT,SAAS;GACP,IAAI,YAA2B;GAC/B,IAAI;IACF,YAAY,QAAQ,QAAQ,cAAc;GAC5C,QAAQ;IACN,YAAY;GACd;GACA,OAAO;IACL,QAAQ,EACN,6BAA6B,KAAK,UAAU,uBAAuB,EACrE;IACA,SAAS;KACP,OAAO,YAAY,CAAC;MAAE,MAAM;MAAgB,aAAa;KAAU,CAAC,IAAI,CAAC;KACzE,QAAQ,CAAC,cAAc;IACzB;IACA,cAAc,EACZ,SAAS;KACP;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;KACA;IACF,EACF;IACA,KAAK;KACH,KAAK;MAAE,SAAS;MAAa,cAAc;KAAe;KAC1D,WAAW;IACb;GACF;EACF;EACA,MAAM,UAAU,MAAM,IAAI;GACxB,IAAI,CAAC,mBAAmB,CAAC,eAAe,KAAK,EAAE,KAAK,GAAG,SAAS,cAAc,GAAG,OAAO;GACxF,IAAI;IACF,MAAM,SAAS,MAAM,iBAAiB,MAAM,IAAI;KAC9C,MAAM,GAAG,SAAS,MAAM,IAAI,QAAQ;KACpC,KAAK;MAAE,SAAS;MAAa,cAAc;KAAe;KAC1D,WAAW;IACb,CAAC;IACD,IAAI,CAAC,QAAQ,MAAM,OAAO;IAC1B,OAAO;KACL,MAAM,0BAA0B,uBAAuB,OAAO,IAAI,IAAI,OAAO;KAC7E,KAAK,OAAO;IACd;GACF,QAAQ;IACN,OAAO;GACT;EACF;CAEU;AACd"}
|
package/dist/server.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/// <reference path="./virtual-askr-server.d.ts" />
|
|
1
2
|
import { ServerApp } from "@askrjs/server";
|
|
2
3
|
//#region src/server/document.d.ts
|
|
3
4
|
interface ViteTelemetryFields {
|
|
@@ -32,5 +33,4 @@ interface AskrServerPlugin {
|
|
|
32
33
|
}
|
|
33
34
|
declare function askrServer(options: AskrServerOptions): AskrServerPlugin;
|
|
34
35
|
//#endregion
|
|
35
|
-
export { ASKR_APP_MARKER, ASKR_HEAD_MARKER, ASKR_SERVER_MODULE_ID, AskrDocumentOptions, type AskrServerOptions, AskrServerPlugin, askrServer, composeAskrDocumentResponse, composeAskrHead, createDocumentApp, insertAskrFragment, isAskrFragment };
|
|
36
|
-
//# sourceMappingURL=server.d.ts.map
|
|
36
|
+
export { ASKR_APP_MARKER, ASKR_HEAD_MARKER, ASKR_SERVER_MODULE_ID, AskrDocumentOptions, type AskrServerOptions, AskrServerPlugin, askrServer, composeAskrDocumentResponse, composeAskrHead, createDocumentApp, insertAskrFragment, isAskrFragment };
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,103 @@
|
|
|
1
|
+
|
|
2
|
+
import { parseFragment, serialize } from "parse5";
|
|
1
3
|
import { createNodeHandler } from "@askrjs/node";
|
|
2
4
|
import { readFile } from "node:fs/promises";
|
|
3
5
|
import { existsSync } from "node:fs";
|
|
4
6
|
import { resolve } from "node:path";
|
|
7
|
+
//#region src/server/head.ts
|
|
8
|
+
const htmlNamespace = "http://www.w3.org/1999/xhtml";
|
|
9
|
+
const linkAttributes = /* @__PURE__ */ new Set([
|
|
10
|
+
"as",
|
|
11
|
+
"crossorigin",
|
|
12
|
+
"data-askr-head",
|
|
13
|
+
"fetchpriority",
|
|
14
|
+
"href",
|
|
15
|
+
"hreflang",
|
|
16
|
+
"imagesizes",
|
|
17
|
+
"imagesrcset",
|
|
18
|
+
"media",
|
|
19
|
+
"referrerpolicy",
|
|
20
|
+
"rel",
|
|
21
|
+
"sizes",
|
|
22
|
+
"type"
|
|
23
|
+
]);
|
|
24
|
+
function headMarkupError(message) {
|
|
25
|
+
throw new TypeError(`Invalid x-askr-head metadata: ${message}`);
|
|
26
|
+
}
|
|
27
|
+
function attributesOf(node) {
|
|
28
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
29
|
+
for (const attribute of node.attrs) {
|
|
30
|
+
if (attribute.namespace || attribute.prefix) headMarkupError("namespaced attributes are not allowed.");
|
|
31
|
+
attributes.set(attribute.name.toLowerCase(), attribute.value);
|
|
32
|
+
}
|
|
33
|
+
return attributes;
|
|
34
|
+
}
|
|
35
|
+
function requireAllowedAttributes(attributes, allowed) {
|
|
36
|
+
for (const name of attributes.keys()) if (!allowed.has(name)) headMarkupError(`attribute ${name} is not allowed.`);
|
|
37
|
+
if (attributes.get("data-askr-head") !== "") headMarkupError("every metadata node must be Askr-owned.");
|
|
38
|
+
}
|
|
39
|
+
function isTextNode(node) {
|
|
40
|
+
return "value" in node;
|
|
41
|
+
}
|
|
42
|
+
function textContent(node) {
|
|
43
|
+
let value = "";
|
|
44
|
+
for (const child of node.childNodes) {
|
|
45
|
+
if (!isTextNode(child)) headMarkupError(`${node.tagName} must contain only text.`);
|
|
46
|
+
value += child.value;
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
}
|
|
50
|
+
function validateHeadElement(node) {
|
|
51
|
+
if (node.namespaceURI !== htmlNamespace) headMarkupError("foreign elements are not allowed.");
|
|
52
|
+
const attributes = attributesOf(node);
|
|
53
|
+
if (node.tagName === "title") {
|
|
54
|
+
requireAllowedAttributes(attributes, /* @__PURE__ */ new Set(["data-askr-head"]));
|
|
55
|
+
textContent(node);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (node.tagName === "meta") {
|
|
59
|
+
requireAllowedAttributes(attributes, /* @__PURE__ */ new Set([
|
|
60
|
+
"content",
|
|
61
|
+
"data-askr-head",
|
|
62
|
+
"name",
|
|
63
|
+
"property"
|
|
64
|
+
]));
|
|
65
|
+
if (attributes.has("name") === attributes.has("property") || !attributes.has("content")) headMarkupError("meta requires content and exactly one of name or property.");
|
|
66
|
+
if (node.childNodes.length) headMarkupError("meta cannot contain child nodes.");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (node.tagName === "link") {
|
|
70
|
+
requireAllowedAttributes(attributes, linkAttributes);
|
|
71
|
+
if (!attributes.has("rel") || !attributes.has("href")) headMarkupError("link requires rel and href.");
|
|
72
|
+
if (node.childNodes.length) headMarkupError("link cannot contain child nodes.");
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
if (node.tagName === "script") {
|
|
76
|
+
requireAllowedAttributes(attributes, /* @__PURE__ */ new Set(["data-askr-head", "type"]));
|
|
77
|
+
if (attributes.get("type") !== "application/ld+json") headMarkupError("only application/ld+json script nodes are allowed.");
|
|
78
|
+
const json = textContent(node);
|
|
79
|
+
try {
|
|
80
|
+
JSON.parse(json);
|
|
81
|
+
} catch {
|
|
82
|
+
headMarkupError("JSON-LD content must be valid JSON.");
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
headMarkupError(`element ${node.tagName} is not allowed.`);
|
|
87
|
+
}
|
|
88
|
+
function normalizeAskrHead(head) {
|
|
89
|
+
const fragment = parseFragment(head);
|
|
90
|
+
for (const node of fragment.childNodes) {
|
|
91
|
+
if (isTextNode(node)) {
|
|
92
|
+
if (node.value.trim()) headMarkupError("top-level text is not allowed.");
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (!("tagName" in node)) headMarkupError("non-element nodes are not allowed.");
|
|
96
|
+
validateHeadElement(node);
|
|
97
|
+
}
|
|
98
|
+
return serialize(fragment);
|
|
99
|
+
}
|
|
100
|
+
//#endregion
|
|
5
101
|
//#region src/server/document.ts
|
|
6
102
|
const ASKR_APP_MARKER = "<!--askr-app-->";
|
|
7
103
|
const ASKR_HEAD_MARKER = "<!--askr-head-->";
|
|
@@ -26,7 +122,7 @@ function insertAskrFragment(document, fragment) {
|
|
|
26
122
|
return document.replace(ASKR_APP_MARKER, fragment);
|
|
27
123
|
}
|
|
28
124
|
function composeAskrHead(document, head, lang, dir) {
|
|
29
|
-
let output = document.replace(ASKR_HEAD_MARKER, head);
|
|
125
|
+
let output = document.replace(ASKR_HEAD_MARKER, normalizeAskrHead(head));
|
|
30
126
|
if (lang) output = patchHtmlAttribute(output, "lang", lang);
|
|
31
127
|
if (dir) output = patchHtmlAttribute(output, "dir", dir);
|
|
32
128
|
return output;
|
|
@@ -38,6 +134,19 @@ function documentHeaders(response) {
|
|
|
38
134
|
const headers = new Headers(response.headers);
|
|
39
135
|
const type = response.headers.get("content-type")?.split(";").map((part) => part.trim()).filter((part) => part.toLowerCase() !== "askr-fragment=1").join("; ");
|
|
40
136
|
if (type) headers.set("content-type", type);
|
|
137
|
+
for (const name of [
|
|
138
|
+
"accept-ranges",
|
|
139
|
+
"content-digest",
|
|
140
|
+
"content-encoding",
|
|
141
|
+
"content-length",
|
|
142
|
+
"content-md5",
|
|
143
|
+
"content-range",
|
|
144
|
+
"digest",
|
|
145
|
+
"etag",
|
|
146
|
+
"last-modified",
|
|
147
|
+
"repr-digest",
|
|
148
|
+
"trailer"
|
|
149
|
+
]) headers.delete(name);
|
|
41
150
|
const internalHeaders = [];
|
|
42
151
|
headers.forEach((_value, key) => {
|
|
43
152
|
if (key.toLowerCase().startsWith("x-askr-")) internalHeaders.push(key);
|
|
@@ -173,5 +282,3 @@ function askrServer(options) {
|
|
|
173
282
|
}
|
|
174
283
|
//#endregion
|
|
175
284
|
export { ASKR_APP_MARKER, ASKR_HEAD_MARKER, ASKR_SERVER_MODULE_ID, askrServer, composeAskrDocumentResponse, composeAskrHead, createDocumentApp, insertAskrFragment, isAskrFragment };
|
|
176
|
-
|
|
177
|
-
//# sourceMappingURL=server.js.map
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../src/server/document.ts","../src/server/development.ts","../src/server/plugin.ts"],"sourcesContent":["import type { ServerApp } from \"@askrjs/server\";\n\ninterface ViteTelemetryFields {\n status?: number;\n}\n\ninterface ViteTelemetry {\n viteDocument?<T>(fields: ViteTelemetryFields, work: () => T): T;\n}\n\nexport interface AskrDocumentOptions {\n telemetry?: ViteTelemetry;\n}\n\nexport const ASKR_APP_MARKER = \"<!--askr-app-->\";\nexport const ASKR_HEAD_MARKER = \"<!--askr-head-->\";\n\nfunction markerCount(document: string): number {\n return document.split(ASKR_APP_MARKER).length - 1;\n}\n\nfunction headMarkerCount(document: string): number {\n return document.split(ASKR_HEAD_MARKER).length - 1;\n}\n\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction patchHtmlAttribute(document: string, name: \"lang\" | \"dir\", value: string): string {\n return document.replace(/<html\\b([^>]*)>/i, (_match, attributes: string) => {\n const matcher = new RegExp(\n `\\\\s+${name}(?:\\\\s*=\\\\s*(?:\"[^\"]*\"|'[^']*'|[^\\\\s\"'=<>\\u0060]+))?`,\n \"gi\",\n );\n const withoutExisting = attributes.replace(matcher, \"\");\n return `<html${withoutExisting} ${name}=\"${escapeAttribute(value)}\">`;\n });\n}\n\nexport function insertAskrFragment(document: string, fragment: string): string {\n const count = markerCount(document);\n if (count !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_APP_MARKER} marker; found ${count}.`,\n );\n }\n return document.replace(ASKR_APP_MARKER, fragment);\n}\n\nexport function composeAskrHead(\n document: string,\n head: string,\n lang?: string,\n dir?: string,\n): string {\n let output = document.replace(ASKR_HEAD_MARKER, head);\n if (lang) output = patchHtmlAttribute(output, \"lang\", lang);\n if (dir) output = patchHtmlAttribute(output, \"dir\", dir);\n return output;\n}\n\nexport function isAskrFragment(response: Response): boolean {\n return (\n response.headers\n .get(\"content-type\")\n ?.split(\";\")\n .some((part) => part.trim().toLowerCase() === \"askr-fragment=1\") ?? false\n );\n}\n\nfunction documentHeaders(response: Response): Headers {\n const headers = new Headers(response.headers);\n const type = response.headers\n .get(\"content-type\")\n ?.split(\";\")\n .map((part) => part.trim())\n .filter((part) => part.toLowerCase() !== \"askr-fragment=1\")\n .join(\"; \");\n if (type) headers.set(\"content-type\", type);\n const internalHeaders: string[] = [];\n headers.forEach((_value, key) => {\n if (key.toLowerCase().startsWith(\"x-askr-\")) internalHeaders.push(key);\n });\n for (const key of internalHeaders) headers.delete(key);\n return headers;\n}\n\nfunction composeDocumentBody(\n fragment: ReadableStream<Uint8Array> | null,\n prefix: string,\n suffix: string,\n): BodyInit {\n if (!fragment) return `${prefix}${suffix}`;\n const encoder = new TextEncoder();\n const reader = fragment.getReader();\n let prefixPending = true;\n let suffixPending = true;\n let reading = false;\n let cancelled = false;\n\n return new ReadableStream<Uint8Array>({\n pull(controller) {\n if (prefixPending) {\n prefixPending = false;\n if (prefix) controller.enqueue(encoder.encode(prefix));\n return;\n }\n if (reading) return;\n reading = true;\n void reader.read().then(\n (part) => {\n reading = false;\n if (cancelled) return;\n if (!part.done) {\n controller.enqueue(part.value);\n return;\n }\n if (suffixPending && suffix) controller.enqueue(encoder.encode(suffix));\n suffixPending = false;\n controller.close();\n },\n (error) => {\n reading = false;\n if (cancelled) return;\n controller.error(error);\n void reader.cancel(error).catch(() => undefined);\n },\n );\n },\n async cancel(reason) {\n cancelled = true;\n await reader.cancel(reason).catch(() => undefined);\n },\n });\n}\n\nexport async function composeAskrDocumentResponse(\n response: Response,\n transformedDocument: string,\n options: AskrDocumentOptions = {},\n): Promise<Response> {\n if (!isAskrFragment(response)) return response;\n const compose = () => {\n if (headMarkerCount(transformedDocument) !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_HEAD_MARKER} marker; found ${headMarkerCount(transformedDocument)}.`,\n );\n }\n const withHead = composeAskrHead(\n transformedDocument,\n response.headers.get(\"x-askr-head\") ?? \"\",\n response.headers.get(\"x-askr-html-lang\") ?? undefined,\n response.headers.get(\"x-askr-html-dir\") ?? undefined,\n );\n const appCount = markerCount(withHead);\n if (appCount !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_APP_MARKER} marker; found ${appCount}.`,\n );\n }\n const markerIndex = withHead.indexOf(ASKR_APP_MARKER);\n const prefix = withHead.slice(0, markerIndex);\n const suffix = withHead.slice(markerIndex + ASKR_APP_MARKER.length);\n return new Response(composeDocumentBody(response.body, prefix, suffix), {\n status: response.status,\n statusText: response.statusText,\n headers: documentHeaders(response),\n });\n };\n return options.telemetry?.viteDocument\n ? options.telemetry.viteDocument({ status: response.status }, compose)\n : compose();\n}\n\nexport function createDocumentApp(\n app: ServerApp,\n transformedDocument: string,\n options: AskrDocumentOptions = {},\n): ServerApp {\n return {\n async fetch(request) {\n return composeAskrDocumentResponse(await app.fetch(request), transformedDocument, options);\n },\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport type { ServerApp } from \"@askrjs/server\";\nimport type { ViteDevServer } from \"vite\";\nimport { composeAskrDocumentResponse, isAskrFragment } from \"./document\";\nimport type { AskrServerOptions } from \"./types\";\n\nfunction serverApp(module: Record<string, unknown>, options: AskrServerOptions): ServerApp {\n const value = module[options.exportName ?? \"app\"] ?? module.default;\n if (!value || typeof (value as ServerApp).fetch !== \"function\") {\n throw new Error(\n `@askrjs/vite: ${options.entry} must export a ServerApp as ${options.exportName ?? \"app\"} or default`,\n );\n }\n return value as ServerApp;\n}\n\nfunction moduleTelemetry(module: Record<string, unknown>) {\n const telemetry = module[\"telemetry\"];\n return telemetry && typeof telemetry === \"object\"\n ? {\n telemetry: telemetry as NonNullable<\n Parameters<typeof composeAskrDocumentResponse>[2]\n >[\"telemetry\"],\n }\n : undefined;\n}\n\nexport function createDevelopmentApp(server: ViteDevServer, options: AskrServerOptions): ServerApp {\n return {\n async fetch(request) {\n const module = await server.ssrLoadModule(options.entry);\n const response = await serverApp(module, options).fetch(request);\n if (!isAskrFragment(response)) return response;\n const file = resolve(server.config.root, options.indexHtml ?? \"index.html\");\n const document = await readFile(file, \"utf8\");\n const transformed = await server.transformIndexHtml(new URL(request.url).pathname, document);\n return composeAskrDocumentResponse(response, transformed, moduleTelemetry(module));\n },\n };\n}\n","import { createNodeHandler } from \"@askrjs/node\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport { createDevelopmentApp } from \"./development\";\nimport type { AskrServerOptions } from \"./types\";\n\nexport const ASKR_SERVER_MODULE_ID = \"virtual:askr-server\";\nconst RESOLVED_SERVER_MODULE_ID = \"\\0askr:server\";\n\n/** Portable public identity for the server integration plugin. */\nexport interface AskrServerPlugin {\n readonly name: \"askr:server\";\n}\n\nexport function askrServer(options: AskrServerOptions): AskrServerPlugin {\n let config: ResolvedConfig | undefined;\n const plugin = {\n name: \"askr:server\" as const,\n config() {\n // Askr owns document routing. Vite should serve modules and assets, but\n // must not rewrite page URLs to /index.html before the Askr fallback.\n // Keep the Askr package family inside Vite's module graph so file-linked\n // peers cannot load a second runtime with a different component context.\n // Vitest derives its dependency inlining from resolve.noExternal, which\n // is also Vite 8's replacement for the deprecated ssr.noExternal option.\n return {\n appType: \"custom\",\n resolve: { noExternal: [\"@askrjs/*\"] },\n };\n },\n configResolved(resolved) {\n config = resolved;\n },\n resolveId(id) {\n return id === ASKR_SERVER_MODULE_ID ? RESOLVED_SERVER_MODULE_ID : null;\n },\n async load(id) {\n if (id !== RESOLVED_SERVER_MODULE_ID) return null;\n if (!config) throw new Error(\"@askrjs/vite: server plugin was not configured.\");\n const sourceDocument = resolve(config.root, options.indexHtml ?? \"index.html\");\n const serverOutDocument = resolve(config.root, config.build.outDir, \"index.html\");\n const clientOutDocument = resolve(config.root, config.build.outDir, \"..\", \"index.html\");\n const builtDocument = [serverOutDocument, clientOutDocument].find(existsSync);\n const document = await readFile(builtDocument ?? sourceDocument, \"utf8\");\n const entry = resolve(config.root, options.entry);\n const exportName = options.exportName ?? \"app\";\n return [\n `import * as source from ${JSON.stringify(entry)};`,\n `import { createDocumentApp } from '@askrjs/vite/server';`,\n `const sourceApp = source[${JSON.stringify(exportName)}] ?? source.default;`,\n `const sourceTelemetry = Reflect.get(source, 'telemetry');`,\n `if (!sourceApp?.fetch) throw new Error(${JSON.stringify(`@askrjs/vite: ${options.entry} must export a ServerApp`)});`,\n `export const app = createDocumentApp(sourceApp, ${JSON.stringify(document)}, sourceTelemetry ? { telemetry: sourceTelemetry } : undefined);`,\n `export default app;`,\n ].join(\"\\n\");\n },\n configureServer(server) {\n const handler = createNodeHandler(createDevelopmentApp(server, options));\n // Page rendering is the development fallback. Register it after Vite's\n // module, asset, and HTML middleware so client entries and imported CSS\n // are served by Vite instead of being mistaken for application routes.\n return () => server.middlewares.use(handler);\n },\n } satisfies Plugin;\n return plugin;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server/head.ts","../src/server/document.ts","../src/server/development.ts","../src/server/plugin.ts"],"sourcesContent":["import { parseFragment, serialize, type DefaultTreeAdapterMap } from \"parse5\";\n\nconst htmlNamespace = \"http://www.w3.org/1999/xhtml\";\nconst linkAttributes = new Set([\n \"as\",\n \"crossorigin\",\n \"data-askr-head\",\n \"fetchpriority\",\n \"href\",\n \"hreflang\",\n \"imagesizes\",\n \"imagesrcset\",\n \"media\",\n \"referrerpolicy\",\n \"rel\",\n \"sizes\",\n \"type\",\n]);\n\nfunction headMarkupError(message: string): never {\n throw new TypeError(`Invalid x-askr-head metadata: ${message}`);\n}\n\nfunction attributesOf(node: DefaultTreeAdapterMap[\"element\"]): Map<string, string> {\n const attributes = new Map<string, string>();\n for (const attribute of node.attrs) {\n if (attribute.namespace || attribute.prefix)\n headMarkupError(\"namespaced attributes are not allowed.\");\n attributes.set(attribute.name.toLowerCase(), attribute.value);\n }\n return attributes;\n}\n\nfunction requireAllowedAttributes(\n attributes: Map<string, string>,\n allowed: ReadonlySet<string>,\n): void {\n for (const name of attributes.keys()) {\n if (!allowed.has(name)) headMarkupError(`attribute ${name} is not allowed.`);\n }\n if (attributes.get(\"data-askr-head\") !== \"\")\n headMarkupError(\"every metadata node must be Askr-owned.\");\n}\n\nfunction isTextNode(\n node: DefaultTreeAdapterMap[\"childNode\"],\n): node is DefaultTreeAdapterMap[\"textNode\"] {\n return \"value\" in node;\n}\n\nfunction textContent(node: DefaultTreeAdapterMap[\"element\"]): string {\n let value = \"\";\n for (const child of node.childNodes) {\n if (!isTextNode(child)) headMarkupError(`${node.tagName} must contain only text.`);\n value += child.value;\n }\n return value;\n}\n\nfunction validateHeadElement(node: DefaultTreeAdapterMap[\"element\"]): void {\n if (node.namespaceURI !== htmlNamespace) headMarkupError(\"foreign elements are not allowed.\");\n const attributes = attributesOf(node);\n if (node.tagName === \"title\") {\n requireAllowedAttributes(attributes, new Set([\"data-askr-head\"]));\n textContent(node);\n return;\n }\n if (node.tagName === \"meta\") {\n requireAllowedAttributes(\n attributes,\n new Set([\"content\", \"data-askr-head\", \"name\", \"property\"]),\n );\n const hasName = attributes.has(\"name\");\n const hasProperty = attributes.has(\"property\");\n if (hasName === hasProperty || !attributes.has(\"content\"))\n headMarkupError(\"meta requires content and exactly one of name or property.\");\n if (node.childNodes.length) headMarkupError(\"meta cannot contain child nodes.\");\n return;\n }\n if (node.tagName === \"link\") {\n requireAllowedAttributes(attributes, linkAttributes);\n if (!attributes.has(\"rel\") || !attributes.has(\"href\"))\n headMarkupError(\"link requires rel and href.\");\n if (node.childNodes.length) headMarkupError(\"link cannot contain child nodes.\");\n return;\n }\n if (node.tagName === \"script\") {\n requireAllowedAttributes(attributes, new Set([\"data-askr-head\", \"type\"]));\n if (attributes.get(\"type\") !== \"application/ld+json\")\n headMarkupError(\"only application/ld+json script nodes are allowed.\");\n const json = textContent(node);\n try {\n JSON.parse(json);\n } catch {\n headMarkupError(\"JSON-LD content must be valid JSON.\");\n }\n return;\n }\n headMarkupError(`element ${node.tagName} is not allowed.`);\n}\n\nexport function normalizeAskrHead(head: string): string {\n const fragment = parseFragment(head);\n for (const node of fragment.childNodes) {\n if (isTextNode(node)) {\n if (node.value.trim()) headMarkupError(\"top-level text is not allowed.\");\n continue;\n }\n if (!(\"tagName\" in node)) headMarkupError(\"non-element nodes are not allowed.\");\n validateHeadElement(node);\n }\n return serialize(fragment);\n}\n","import type { ServerApp } from \"@askrjs/server\";\nimport { normalizeAskrHead } from \"./head\";\n\ninterface ViteTelemetryFields {\n status?: number;\n}\n\ninterface ViteTelemetry {\n viteDocument?<T>(fields: ViteTelemetryFields, work: () => T): T;\n}\n\nexport interface AskrDocumentOptions {\n telemetry?: ViteTelemetry;\n}\n\nexport const ASKR_APP_MARKER = \"<!--askr-app-->\";\nexport const ASKR_HEAD_MARKER = \"<!--askr-head-->\";\n\nfunction markerCount(document: string): number {\n return document.split(ASKR_APP_MARKER).length - 1;\n}\n\nfunction headMarkerCount(document: string): number {\n return document.split(ASKR_HEAD_MARKER).length - 1;\n}\n\nfunction escapeAttribute(value: string): string {\n return value\n .replace(/&/g, \"&\")\n .replace(/</g, \"<\")\n .replace(/>/g, \">\")\n .replace(/\"/g, \""\")\n .replace(/'/g, \"'\");\n}\n\nfunction patchHtmlAttribute(document: string, name: \"lang\" | \"dir\", value: string): string {\n return document.replace(/<html\\b([^>]*)>/i, (_match, attributes: string) => {\n const matcher = new RegExp(\n `\\\\s+${name}(?:\\\\s*=\\\\s*(?:\"[^\"]*\"|'[^']*'|[^\\\\s\"'=<>\\u0060]+))?`,\n \"gi\",\n );\n const withoutExisting = attributes.replace(matcher, \"\");\n return `<html${withoutExisting} ${name}=\"${escapeAttribute(value)}\">`;\n });\n}\n\nexport function insertAskrFragment(document: string, fragment: string): string {\n const count = markerCount(document);\n if (count !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_APP_MARKER} marker; found ${count}.`,\n );\n }\n return document.replace(ASKR_APP_MARKER, fragment);\n}\n\nexport function composeAskrHead(\n document: string,\n head: string,\n lang?: string,\n dir?: string,\n): string {\n let output = document.replace(ASKR_HEAD_MARKER, normalizeAskrHead(head));\n if (lang) output = patchHtmlAttribute(output, \"lang\", lang);\n if (dir) output = patchHtmlAttribute(output, \"dir\", dir);\n return output;\n}\n\nexport function isAskrFragment(response: Response): boolean {\n return (\n response.headers\n .get(\"content-type\")\n ?.split(\";\")\n .some((part) => part.trim().toLowerCase() === \"askr-fragment=1\") ?? false\n );\n}\n\nfunction documentHeaders(response: Response): Headers {\n const headers = new Headers(response.headers);\n const type = response.headers\n .get(\"content-type\")\n ?.split(\";\")\n .map((part) => part.trim())\n .filter((part) => part.toLowerCase() !== \"askr-fragment=1\")\n .join(\"; \");\n if (type) headers.set(\"content-type\", type);\n for (const name of [\n \"accept-ranges\",\n \"content-digest\",\n \"content-encoding\",\n \"content-length\",\n \"content-md5\",\n \"content-range\",\n \"digest\",\n \"etag\",\n \"last-modified\",\n \"repr-digest\",\n \"trailer\",\n ]) {\n headers.delete(name);\n }\n const internalHeaders: string[] = [];\n headers.forEach((_value, key) => {\n if (key.toLowerCase().startsWith(\"x-askr-\")) internalHeaders.push(key);\n });\n for (const key of internalHeaders) headers.delete(key);\n return headers;\n}\n\nfunction composeDocumentBody(\n fragment: ReadableStream<Uint8Array> | null,\n prefix: string,\n suffix: string,\n): BodyInit {\n if (!fragment) return `${prefix}${suffix}`;\n const encoder = new TextEncoder();\n const reader = fragment.getReader();\n let prefixPending = true;\n let suffixPending = true;\n let reading = false;\n let cancelled = false;\n\n return new ReadableStream<Uint8Array>({\n pull(controller) {\n if (prefixPending) {\n prefixPending = false;\n if (prefix) controller.enqueue(encoder.encode(prefix));\n return;\n }\n if (reading) return;\n reading = true;\n void reader.read().then(\n (part) => {\n reading = false;\n if (cancelled) return;\n if (!part.done) {\n controller.enqueue(part.value);\n return;\n }\n if (suffixPending && suffix) controller.enqueue(encoder.encode(suffix));\n suffixPending = false;\n controller.close();\n },\n (error) => {\n reading = false;\n if (cancelled) return;\n controller.error(error);\n void reader.cancel(error).catch(() => undefined);\n },\n );\n },\n async cancel(reason) {\n cancelled = true;\n await reader.cancel(reason).catch(() => undefined);\n },\n });\n}\n\nexport async function composeAskrDocumentResponse(\n response: Response,\n transformedDocument: string,\n options: AskrDocumentOptions = {},\n): Promise<Response> {\n if (!isAskrFragment(response)) return response;\n const compose = () => {\n if (headMarkerCount(transformedDocument) !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_HEAD_MARKER} marker; found ${headMarkerCount(transformedDocument)}.`,\n );\n }\n const withHead = composeAskrHead(\n transformedDocument,\n response.headers.get(\"x-askr-head\") ?? \"\",\n response.headers.get(\"x-askr-html-lang\") ?? undefined,\n response.headers.get(\"x-askr-html-dir\") ?? undefined,\n );\n const appCount = markerCount(withHead);\n if (appCount !== 1) {\n throw new Error(\n `index.html must contain exactly one ${ASKR_APP_MARKER} marker; found ${appCount}.`,\n );\n }\n const markerIndex = withHead.indexOf(ASKR_APP_MARKER);\n const prefix = withHead.slice(0, markerIndex);\n const suffix = withHead.slice(markerIndex + ASKR_APP_MARKER.length);\n return new Response(composeDocumentBody(response.body, prefix, suffix), {\n status: response.status,\n statusText: response.statusText,\n headers: documentHeaders(response),\n });\n };\n return options.telemetry?.viteDocument\n ? options.telemetry.viteDocument({ status: response.status }, compose)\n : compose();\n}\n\nexport function createDocumentApp(\n app: ServerApp,\n transformedDocument: string,\n options: AskrDocumentOptions = {},\n): ServerApp {\n return {\n async fetch(request) {\n return composeAskrDocumentResponse(await app.fetch(request), transformedDocument, options);\n },\n };\n}\n","import { readFile } from \"node:fs/promises\";\nimport { resolve } from \"node:path\";\nimport type { ServerApp } from \"@askrjs/server\";\nimport type { ViteDevServer } from \"vite\";\nimport { composeAskrDocumentResponse, isAskrFragment } from \"./document\";\nimport type { AskrServerOptions } from \"./types\";\n\nfunction serverApp(module: Record<string, unknown>, options: AskrServerOptions): ServerApp {\n const value = module[options.exportName ?? \"app\"] ?? module.default;\n if (!value || typeof (value as ServerApp).fetch !== \"function\") {\n throw new Error(\n `@askrjs/vite: ${options.entry} must export a ServerApp as ${options.exportName ?? \"app\"} or default`,\n );\n }\n return value as ServerApp;\n}\n\nfunction moduleTelemetry(module: Record<string, unknown>) {\n const telemetry = module[\"telemetry\"];\n return telemetry && typeof telemetry === \"object\"\n ? {\n telemetry: telemetry as NonNullable<\n Parameters<typeof composeAskrDocumentResponse>[2]\n >[\"telemetry\"],\n }\n : undefined;\n}\n\nexport function createDevelopmentApp(server: ViteDevServer, options: AskrServerOptions): ServerApp {\n return {\n async fetch(request) {\n const module = await server.ssrLoadModule(options.entry);\n const response = await serverApp(module, options).fetch(request);\n if (!isAskrFragment(response)) return response;\n const file = resolve(server.config.root, options.indexHtml ?? \"index.html\");\n const document = await readFile(file, \"utf8\");\n const transformed = await server.transformIndexHtml(new URL(request.url).pathname, document);\n return composeAskrDocumentResponse(response, transformed, moduleTelemetry(module));\n },\n };\n}\n","import { createNodeHandler } from \"@askrjs/node\";\nimport { readFile } from \"node:fs/promises\";\nimport { existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport type { Plugin, ResolvedConfig } from \"vite\";\nimport { createDevelopmentApp } from \"./development\";\nimport type { AskrServerOptions } from \"./types\";\n\nexport const ASKR_SERVER_MODULE_ID = \"virtual:askr-server\";\nconst RESOLVED_SERVER_MODULE_ID = \"\\0askr:server\";\n\n/** Portable public identity for the server integration plugin. */\nexport interface AskrServerPlugin {\n readonly name: \"askr:server\";\n}\n\nexport function askrServer(options: AskrServerOptions): AskrServerPlugin {\n let config: ResolvedConfig | undefined;\n const plugin = {\n name: \"askr:server\" as const,\n config() {\n // Askr owns document routing. Vite should serve modules and assets, but\n // must not rewrite page URLs to /index.html before the Askr fallback.\n // Keep the Askr package family inside Vite's module graph so file-linked\n // peers cannot load a second runtime with a different component context.\n // Vitest derives its dependency inlining from resolve.noExternal, which\n // is also Vite 8's replacement for the deprecated ssr.noExternal option.\n return {\n appType: \"custom\",\n resolve: { noExternal: [\"@askrjs/*\"] },\n };\n },\n configResolved(resolved) {\n config = resolved;\n },\n resolveId(id) {\n return id === ASKR_SERVER_MODULE_ID ? RESOLVED_SERVER_MODULE_ID : null;\n },\n async load(id) {\n if (id !== RESOLVED_SERVER_MODULE_ID) return null;\n if (!config) throw new Error(\"@askrjs/vite: server plugin was not configured.\");\n const sourceDocument = resolve(config.root, options.indexHtml ?? \"index.html\");\n const serverOutDocument = resolve(config.root, config.build.outDir, \"index.html\");\n const clientOutDocument = resolve(config.root, config.build.outDir, \"..\", \"index.html\");\n const builtDocument = [serverOutDocument, clientOutDocument].find(existsSync);\n const document = await readFile(builtDocument ?? sourceDocument, \"utf8\");\n const entry = resolve(config.root, options.entry);\n const exportName = options.exportName ?? \"app\";\n return [\n `import * as source from ${JSON.stringify(entry)};`,\n `import { createDocumentApp } from '@askrjs/vite/server';`,\n `const sourceApp = source[${JSON.stringify(exportName)}] ?? source.default;`,\n `const sourceTelemetry = Reflect.get(source, 'telemetry');`,\n `if (!sourceApp?.fetch) throw new Error(${JSON.stringify(`@askrjs/vite: ${options.entry} must export a ServerApp`)});`,\n `export const app = createDocumentApp(sourceApp, ${JSON.stringify(document)}, sourceTelemetry ? { telemetry: sourceTelemetry } : undefined);`,\n `export default app;`,\n ].join(\"\\n\");\n },\n configureServer(server) {\n const handler = createNodeHandler(createDevelopmentApp(server, options));\n // Page rendering is the development fallback. Register it after Vite's\n // module, asset, and HTML middleware so client entries and imported CSS\n // are served by Vite instead of being mistaken for application routes.\n return () => server.middlewares.use(handler);\n },\n } satisfies Plugin;\n return plugin;\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askrjs/vite",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"description": "Vite plugin for Askr JSX and template transforms",
|
|
5
5
|
"homepage": "https://github.com/askrjs/askr-vite#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -39,21 +39,20 @@
|
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "vp pack",
|
|
41
41
|
"dev": "vp pack --watch",
|
|
42
|
-
"test": "npm run test:unit
|
|
42
|
+
"test": "npm run test:unit",
|
|
43
43
|
"test:unit": "vp test run -c vitest.config.ts",
|
|
44
|
-
"test:jsdom": "npm run test:unit",
|
|
45
|
-
"test:browser": "npm run test:unit",
|
|
46
44
|
"test:publint": "publint",
|
|
47
45
|
"fmt": "vp fmt .",
|
|
48
46
|
"lint": "vp lint .",
|
|
49
47
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
50
|
-
"pack:check": "
|
|
48
|
+
"pack:check": "node tests/package-artifacts.js",
|
|
51
49
|
"check": "npm run lint && npm run typecheck && npm test && npm run build && npm run test:publint && npm run pack:check",
|
|
52
50
|
"prepack": "npm run build",
|
|
53
51
|
"prepublishOnly": "npm run check"
|
|
54
52
|
},
|
|
55
53
|
"dependencies": {
|
|
56
|
-
"@askrjs/node": ">=0.0.1 <0.1.0"
|
|
54
|
+
"@askrjs/node": ">=0.0.1 <0.1.0",
|
|
55
|
+
"parse5": "^7.3.0"
|
|
57
56
|
},
|
|
58
57
|
"devDependencies": {
|
|
59
58
|
"@askrjs/askr": ">=0.0.52 <0.1.0",
|
|
@@ -62,7 +61,8 @@
|
|
|
62
61
|
"publint": "^0.3.21",
|
|
63
62
|
"typescript": "^6.0.3",
|
|
64
63
|
"vite": "^8.0.16",
|
|
65
|
-
"vite-plus": "^0.2.4"
|
|
64
|
+
"vite-plus": "^0.2.4",
|
|
65
|
+
"vitest": "^4.1.10"
|
|
66
66
|
},
|
|
67
67
|
"peerDependencies": {
|
|
68
68
|
"@askrjs/askr": ">=0.0.52 <0.1.0",
|