mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-23 03:27:27 +09:00
fix: export text pages in static builds (#6513)
Fixes #6440 ## Description Static exports currently omit Text pages such as `/robots.txt`, `/ads.txt`, and `/.well-known/security.txt`. This generates their routes, evaluates content through the existing page-data loader, and uses Vike's native `pageContext.content` to write the exact text at the configured file path. Internal route directories are separate from public URLs so dot-prefixed paths work. No generated writer script, text-page manifest, HTML wrapper, or post-build file rewrite is needed. ## Dependencies - Pin Vike to `0.4.267-commit-feffb0f`: native non-HTML output is published in this commit prerelease but is not yet in stable `0.4.267`. - Use Vite `7.1.12` and the compatible React plugin for SSG. Build through `vike build && vike prerender` so Vike uses its matching Vite dependency. - Update React Router's `vite-node` dependency to `3.2.4` for compatibility with the CLI's Vite 7 test environment. ## Validation - All 1,173 CLI tests pass locally across 72 files, including all 79 prebuild tests for normal HTML, dynamic SSG routes, and React Router builds. - Text export coverage checks exact output for `/robots.txt`, resource-backed `/.well-known/security.txt`, empty text, Unicode, and HTML-like content. - CLI and SSG fixture type checks, lint, and formatting pass locally. - SSG fixtures regenerated; the packaged SSG test passes locally. - All executed GitHub CI checks pass at `201fa971616bb607ca35ee24a970f107eddee754`, including all six browser shards. Shard 4 passed on retry after an MDX template rename timeout. Visual regression is skipped and preview deployment remains gated. - Corrected the existing animation navigation test for fork CI: wait for the rendered component with the public fallback and retain the animation initialization assertion when the private module is present. ## Manual verification 1. Export a static project with Text pages at `/robots.txt` and `/.well-known/security.txt`. 2. Run the generated project's build. 3. Confirm both files under `dist/client` contain exactly the configured text, including resource expressions. 4. Confirm normal pages still render HTML. - [ ] Original author tested the updated export and confirmed it works. - [ ] Preview tested (deployment approval remains gated). --------- Co-authored-by: Sviat Barbutsa <9897782+sviat-barbutsa@users.noreply.github.com> Co-authored-by: Oleg Isonen <oleg008@gmail.com>
This commit is contained in:
14 files changed
+1162
-262
No files matched your search
@@ -229,7 +229,7 @@ test.beforeAll(async ({ browser }, workerInfo) => {
|
||||
});
|
||||
});
|
||||
|
||||
test("Canvas switches away from text animation without reloading", async ({
|
||||
test("Canvas switches away from an AnimateText page without reloading", async ({
|
||||
browser,
|
||||
page,
|
||||
}, workerInfo) => {
|
||||
@@ -303,7 +303,12 @@ test("Canvas switches away from text animation without reloading", async ({
|
||||
if (canvas === undefined) {
|
||||
throw new Error("Expected canvas frame");
|
||||
}
|
||||
await canvas.locator("[data-ws-text-animate]").first().waitFor();
|
||||
await canvas
|
||||
.locator(
|
||||
'[data-ws-component="@webstudio-is/sdk-components-animation:AnimateText"]'
|
||||
)
|
||||
.getByText("Animated page text")
|
||||
.waitFor();
|
||||
await openPage({
|
||||
page,
|
||||
pageName: "Home",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "vite build && vike prerender",
|
||||
"build": "vike build && vike prerender",
|
||||
"build:debug": "pnpm build && cd dist && oxfmt '**/*.html'",
|
||||
"dev": "vite dev",
|
||||
"typecheck": "tsc --noEmit",
|
||||
@@ -26,9 +26,9 @@
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.70",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "^6.3.4",
|
||||
"vite": "7.1.12",
|
||||
"webstudio": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"@webstudio-is/sdk-components-react-remix": "workspace:*",
|
||||
"react": "18.3.0-canary-14898b6a9-20240318",
|
||||
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
||||
"vike": "^0.4.229",
|
||||
"vike": "0.4.267-commit-feffb0f",
|
||||
"@webstudio-is/wsauth": "workspace:*",
|
||||
"@shikijs/langs": "4.4.1",
|
||||
"@shikijs/themes": "4.4.1",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"build": "vite build && vike prerender",
|
||||
"build": "vike build && vike prerender",
|
||||
"build:debug": "pnpm build && cd dist && oxfmt '**/*.html'",
|
||||
"dev": "vite dev",
|
||||
"typecheck": "tsc --noEmit",
|
||||
@@ -26,9 +26,9 @@
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.70",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "^6.3.4",
|
||||
"vite": "7.1.12",
|
||||
"webstudio": "workspace:*"
|
||||
},
|
||||
"dependencies": {
|
||||
@@ -41,7 +41,7 @@
|
||||
"@webstudio-is/sdk-components-react-remix": "workspace:*",
|
||||
"react": "18.3.0-canary-14898b6a9-20240318",
|
||||
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
||||
"vike": "^0.4.229",
|
||||
"vike": "0.4.267-commit-feffb0f",
|
||||
"@webstudio-is/wsauth": "workspace:*",
|
||||
"@shikijs/langs": "4.4.1",
|
||||
"@shikijs/themes": "4.4.1",
|
||||
|
||||
+2
-1
@@ -97,7 +97,8 @@
|
||||
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
||||
"playwright": "1.50.1",
|
||||
"@playwright/test": "1.50.1",
|
||||
"vite@<7": "6.3.4"
|
||||
"vite@<7": "6.3.4",
|
||||
"@react-router/dev>vite-node": "3.2.4"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"@stitches/react@1.3.1-1": "patches/@stitches__react@1.3.1-1.patch",
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
"@types/which": "^3.0.4",
|
||||
"@types/yargs": "^17.0.33",
|
||||
"@vercel/react-router": "^1.1.0",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"@webstudio-is/css-engine": "workspace:*",
|
||||
"@webstudio-is/content-engine": "workspace:*",
|
||||
"@webstudio-is/expression": "workspace:*",
|
||||
@@ -128,8 +128,8 @@
|
||||
"shiki": "4.4.1",
|
||||
"ts-expect": "^1.3.0",
|
||||
"typescript": "7.0.2",
|
||||
"vike": "^0.4.229",
|
||||
"vite": "^6.3.4",
|
||||
"vike": "0.4.267-commit-feffb0f",
|
||||
"vite": "7.1.12",
|
||||
"vitest": "^3.1.2",
|
||||
"wrangler": "4.130.0"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { join } from "node:path";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { join, relative } from "node:path";
|
||||
import { readFile, readdir } from "node:fs/promises";
|
||||
import { createHash } from "node:crypto";
|
||||
import { isPathnamePattern, matchPathnameParams } from "@webstudio-is/sdk";
|
||||
import {
|
||||
baseComponentImportSource,
|
||||
@@ -28,23 +29,65 @@ const generateVikeRoute = (pagePath: string) => {
|
||||
return route;
|
||||
};
|
||||
|
||||
const generateVikeTextRoute = (pagePath: string) =>
|
||||
`text-${createHash("sha256").update(pagePath).digest("hex")}`;
|
||||
|
||||
const readTemplates = async (directory: string) => {
|
||||
const entries = await readdir(directory, {
|
||||
recursive: true,
|
||||
withFileTypes: true,
|
||||
});
|
||||
return Object.fromEntries(
|
||||
await Promise.all(
|
||||
entries
|
||||
.filter((entry) => entry.isFile())
|
||||
.map(async (entry) => {
|
||||
const file = join(entry.parentPath, entry.name);
|
||||
// Raw code placeholders use .template to stay out of typechecking.
|
||||
return [
|
||||
relative(directory, file).replace(/\.template$/, ""),
|
||||
await readFile(file, "utf8"),
|
||||
] as const;
|
||||
})
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export const createFramework = async (
|
||||
options: FrameworkOptions = {}
|
||||
): Promise<Framework> => {
|
||||
const templatesDirectory = getFrameworkTemplatesDirectory(options);
|
||||
const htmlPageTemplate = await readFile(
|
||||
join(templatesDirectory, "html", "+Page.tsx"),
|
||||
"utf8"
|
||||
);
|
||||
const htmlHeadTemplate = await readFile(
|
||||
join(templatesDirectory, "html", "+Head.tsx"),
|
||||
"utf8"
|
||||
);
|
||||
const htmlDataTemplate = await readFile(
|
||||
join(templatesDirectory, "html", "+data.ts"),
|
||||
"utf8"
|
||||
const [sharedTemplates, htmlTemplates, textTemplates, dynamicTemplates] =
|
||||
await Promise.all(
|
||||
["shared", "html", "text", "dynamic"].map((directory) =>
|
||||
readTemplates(join(templatesDirectory, directory))
|
||||
)
|
||||
);
|
||||
|
||||
const getRouteTemplates = (
|
||||
type: "html" | "text",
|
||||
pagePath: string,
|
||||
prerenderPaths: readonly string[] = []
|
||||
) => {
|
||||
const route =
|
||||
type === "html"
|
||||
? generateVikeRoute(pagePath)
|
||||
: generateVikeTextRoute(pagePath);
|
||||
const templates = {
|
||||
...sharedTemplates,
|
||||
...(type === "html" ? htmlTemplates : textTemplates),
|
||||
...(isPathnamePattern(pagePath) ? dynamicTemplates : {}),
|
||||
};
|
||||
return Object.entries(templates).map(([file, template]) => ({
|
||||
file: join("pages", route, file),
|
||||
template: template
|
||||
.replaceAll("__PAGE_PATH__", () => JSON.stringify(pagePath))
|
||||
.replaceAll("__PRERENDER_PATHS__", () =>
|
||||
JSON.stringify(prerenderPaths)
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
// cleanup route templates after reading to not bloat generated code
|
||||
await cleanupFrameworkTemplates(options);
|
||||
|
||||
@@ -68,33 +111,11 @@ export const createFramework = async (
|
||||
if (dynamic && prerenderPaths.length === 0) {
|
||||
return [];
|
||||
}
|
||||
const route = generateVikeRoute(pagePath);
|
||||
const entries = [
|
||||
{
|
||||
file: join("pages", route, "+Page.tsx"),
|
||||
template: htmlPageTemplate,
|
||||
},
|
||||
{
|
||||
file: join("pages", route, "+Head.tsx"),
|
||||
template: htmlHeadTemplate,
|
||||
},
|
||||
{
|
||||
file: join("pages", route, "+data.ts"),
|
||||
template: htmlDataTemplate,
|
||||
},
|
||||
];
|
||||
if (dynamic) {
|
||||
entries.push({
|
||||
file: join("pages", route, "+onBeforePrerenderStart.ts"),
|
||||
template: `export const onBeforePrerenderStart = () => ${JSON.stringify(
|
||||
prerenderPaths
|
||||
)};\n`,
|
||||
});
|
||||
}
|
||||
return entries;
|
||||
return getRouteTemplates("html", pagePath, prerenderPaths);
|
||||
},
|
||||
xml: () => [],
|
||||
text: () => [],
|
||||
text: ({ pagePath }) =>
|
||||
isPathnamePattern(pagePath) ? [] : getRouteTemplates("text", pagePath),
|
||||
defaultSitemap: () => [],
|
||||
};
|
||||
};
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
import { build } from "esbuild";
|
||||
import { loadConfigFromFile } from "vite";
|
||||
import { bundleVersion } from "@webstudio-is/protocol";
|
||||
import type { Asset, Instance, Prop } from "@webstudio-is/sdk";
|
||||
import type { Asset, DataSource, Instance, Prop } from "@webstudio-is/sdk";
|
||||
import {
|
||||
createDocumentGraph,
|
||||
type AssetFileDocument,
|
||||
@@ -94,6 +94,11 @@ const runGeneratedCommand = async (
|
||||
});
|
||||
};
|
||||
|
||||
const prerenderSsg = async () => {
|
||||
await runGeneratedCommand("vike", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
};
|
||||
|
||||
const linkPackagedPreviewDependencies = async () => {
|
||||
const sourceNodeModules = join(originalCwd, "node_modules");
|
||||
const targetNodeModules = join(tempDir, "node_modules");
|
||||
@@ -243,6 +248,8 @@ const createSiteData = (
|
||||
}>;
|
||||
instances?: Array<[string, Omit<Instance, "type">]>;
|
||||
props?: Array<[string, Prop]>;
|
||||
dataSources?: Array<[string, DataSource]>;
|
||||
resources?: Array<[string, Resource]>;
|
||||
pageMeta?: Record<string, unknown>;
|
||||
redirects?: Redirects;
|
||||
} = {}
|
||||
@@ -335,8 +342,8 @@ const createSiteData = (
|
||||
],
|
||||
]
|
||||
).map(([id, instance]) => [id, { type: "instance", ...instance }]),
|
||||
dataSources: [],
|
||||
resources: [],
|
||||
dataSources: overrides.dataSources ?? [],
|
||||
resources: overrides.resources ?? [],
|
||||
styleSources: [],
|
||||
styleSourceSelections: [],
|
||||
styles: [],
|
||||
@@ -790,7 +797,7 @@ test("hydrates encoded filenames from an embedded SSG database", async () => {
|
||||
describe("prebuild", () => {
|
||||
test("rejects Assets queries without a content database without changing generated files", async () => {
|
||||
const siteData = createSiteData();
|
||||
siteData.build.resources = [["posts", createQueryResource()]] as never;
|
||||
siteData.build.resources = [["posts", createQueryResource()]];
|
||||
siteData.build.dataSources = [
|
||||
[
|
||||
"posts-data",
|
||||
@@ -802,7 +809,7 @@ describe("prebuild", () => {
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(siteData);
|
||||
const existingFiles = [
|
||||
"app/routes/_index.tsx",
|
||||
@@ -1266,7 +1273,7 @@ describe("prebuild", () => {
|
||||
resourceId: "posts",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.resources = [
|
||||
[
|
||||
"posts",
|
||||
@@ -1290,7 +1297,7 @@ describe("prebuild", () => {
|
||||
}),
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.styleSources = [
|
||||
["dynamic-style", { type: "local", id: "dynamic-style" }],
|
||||
] as never;
|
||||
@@ -1439,7 +1446,7 @@ describe("prebuild", () => {
|
||||
name,
|
||||
resourceId: `${name}-resource`,
|
||||
},
|
||||
]) as never;
|
||||
]);
|
||||
siteData.build.resources = resourceNames.map((name) => [
|
||||
`${name}-resource`,
|
||||
{
|
||||
@@ -1449,7 +1456,7 @@ describe("prebuild", () => {
|
||||
method: "get",
|
||||
headers: [],
|
||||
},
|
||||
]) as never;
|
||||
]);
|
||||
await writeSiteData(siteData);
|
||||
|
||||
await prebuild({
|
||||
@@ -1586,8 +1593,7 @@ describe("prebuild", () => {
|
||||
});
|
||||
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
await runGeneratedCommand("vite", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
await prerenderSsg();
|
||||
|
||||
const html = parseHtml(await readFile("dist/client/index.html", "utf8"));
|
||||
const codeElements = findElementsByTagName(html, "code");
|
||||
@@ -1651,8 +1657,7 @@ describe("prebuild", () => {
|
||||
|
||||
await prebuild({ assets: false, template: ["ssg"] });
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
await runGeneratedCommand("vite", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
await prerenderSsg();
|
||||
|
||||
const htmlSource = await readFile("dist/client/index.html", "utf8");
|
||||
expect(htmlSource).toMatch(/^<!DOCTYPE html><html/);
|
||||
@@ -2346,9 +2351,7 @@ sitemap.map((page) => page.path);`
|
||||
],
|
||||
assetIndex: index,
|
||||
};
|
||||
siteData.build.resources = [
|
||||
["posts", createQueryResource("full")],
|
||||
] as never;
|
||||
siteData.build.resources = [["posts", createQueryResource("full")]];
|
||||
siteData.build.dataSources = [
|
||||
[
|
||||
"posts-data",
|
||||
@@ -2360,7 +2363,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(
|
||||
siteData as unknown as ReturnType<typeof createSiteData>
|
||||
);
|
||||
@@ -2512,7 +2515,7 @@ sitemap.map((page) => page.path);`
|
||||
}),
|
||||
assetIndex: index,
|
||||
};
|
||||
siteData.build.resources = [["posts", createQueryResource()]] as never;
|
||||
siteData.build.resources = [["posts", createQueryResource()]];
|
||||
siteData.build.dataSources = [
|
||||
[
|
||||
"posts-data",
|
||||
@@ -2524,7 +2527,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(
|
||||
siteData as unknown as ReturnType<typeof createSiteData>
|
||||
);
|
||||
@@ -2864,7 +2867,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.resources = [
|
||||
[
|
||||
"author",
|
||||
@@ -2887,7 +2890,7 @@ sitemap.map((page) => page.path);`
|
||||
headers: [{ name: "Cache-Control", value: '"public, max-age=60"' }],
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(siteData);
|
||||
await prebuild({ assets: false, template: [template] });
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
@@ -2998,13 +3001,12 @@ sitemap.map((page) => page.path);`
|
||||
headers: [],
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(siteData);
|
||||
|
||||
await prebuild({ assets: false, template: ["ssg"] });
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
await runGeneratedCommand("vite", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
await prerenderSsg();
|
||||
|
||||
const html = parseHtml(await readFile("dist/client/index.html", "utf8"));
|
||||
const [form] = findElementsByTagName(html, "form");
|
||||
@@ -3133,7 +3135,7 @@ sitemap.map((page) => page.path);`
|
||||
}),
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.dataSources = [
|
||||
[
|
||||
"post-data",
|
||||
@@ -3145,7 +3147,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "post-root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(siteData);
|
||||
|
||||
await prebuild({ assets: false, template: ["ssg"] });
|
||||
@@ -3163,8 +3165,7 @@ sitemap.map((page) => page.path);`
|
||||
expect(sitemap).not.toContain('"path": "/blog/hello-world"');
|
||||
expect(sitemap).not.toContain('"path": "/blog/draft-post"');
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
await runGeneratedCommand("vite", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
await prerenderSsg();
|
||||
await expect(
|
||||
readFile("dist/client/blog/hello-world/index.html", "utf8")
|
||||
).resolves.toContain("<!DOCTYPE html>");
|
||||
@@ -3652,7 +3653,7 @@ sitemap.map((page) => page.path);`
|
||||
}),
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.dataSources = [
|
||||
[
|
||||
"posts-data",
|
||||
@@ -3664,7 +3665,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(
|
||||
siteData as unknown as ReturnType<typeof createSiteData>
|
||||
);
|
||||
@@ -3701,8 +3702,7 @@ sitemap.map((page) => page.path);`
|
||||
meta: { totalCount: 1, hasMore: false },
|
||||
},
|
||||
]);
|
||||
await runGeneratedCommand("vite", ["build"]);
|
||||
await runGeneratedCommand("vike", ["prerender"]);
|
||||
await prerenderSsg();
|
||||
await expect(readFile("dist/client/index.html", "utf8")).resolves.toContain(
|
||||
"<!DOCTYPE html>"
|
||||
);
|
||||
@@ -3806,7 +3806,7 @@ sitemap.map((page) => page.path);`
|
||||
scopeInstanceId: "collection",
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
siteData.build.resources = [
|
||||
[
|
||||
"sitemap-resource",
|
||||
@@ -3819,7 +3819,7 @@ sitemap.map((page) => page.path);`
|
||||
headers: [],
|
||||
},
|
||||
],
|
||||
] as never;
|
||||
];
|
||||
await writeSiteData(siteData);
|
||||
|
||||
await prebuild({ assets: false, template: ["react-router"] });
|
||||
@@ -3829,6 +3829,96 @@ sitemap.map((page) => page.path);`
|
||||
).resolves.toContain('rootIds: [\n "sitemap-resource"');
|
||||
});
|
||||
|
||||
test("emits static text pages at their configured paths", async () => {
|
||||
const robots = "User-agent: *\nDisallow: /private";
|
||||
const security = "Contact: mailto:security@example.com\n# <head> & café\n";
|
||||
const siteData = createSiteData({
|
||||
pages: [
|
||||
{
|
||||
id: "home",
|
||||
name: "Home",
|
||||
title: "Home",
|
||||
path: "",
|
||||
rootInstanceId: "root",
|
||||
meta: {},
|
||||
},
|
||||
{
|
||||
id: "robots",
|
||||
name: "Robots",
|
||||
title: "Robots",
|
||||
path: "/robots.txt",
|
||||
rootInstanceId: "root",
|
||||
meta: {
|
||||
documentType: "text",
|
||||
content: JSON.stringify(robots),
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "security",
|
||||
name: "Security",
|
||||
title: "Security",
|
||||
path: "/.well-known/security.txt",
|
||||
rootInstanceId: "root",
|
||||
meta: {
|
||||
documentType: "text",
|
||||
content: `${encodeDataVariableId("security-data")}.data.content`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "empty",
|
||||
name: "Empty",
|
||||
title: "Empty",
|
||||
path: "/empty.txt",
|
||||
rootInstanceId: "root",
|
||||
meta: { documentType: "text", content: '""' },
|
||||
},
|
||||
],
|
||||
dataSources: [
|
||||
[
|
||||
"security-data",
|
||||
{
|
||||
id: "security-data",
|
||||
type: "resource",
|
||||
name: "Security",
|
||||
resourceId: "security-resource",
|
||||
scopeInstanceId: "root",
|
||||
},
|
||||
],
|
||||
],
|
||||
resources: [
|
||||
[
|
||||
"security-resource",
|
||||
{
|
||||
id: "security-resource",
|
||||
name: "Security",
|
||||
control: "system",
|
||||
method: "get",
|
||||
url: JSON.stringify(
|
||||
`data:application/json,${encodeURIComponent(JSON.stringify({ content: security }))}`
|
||||
),
|
||||
headers: [],
|
||||
},
|
||||
],
|
||||
],
|
||||
});
|
||||
await writeSiteData(siteData);
|
||||
|
||||
await prebuild({ assets: false, template: ["ssg"] });
|
||||
await symlink(join(originalCwd, "node_modules"), "node_modules", "dir");
|
||||
await prerenderSsg();
|
||||
|
||||
await expect(readFile("dist/client/robots.txt", "utf8")).resolves.toBe(
|
||||
robots
|
||||
);
|
||||
await expect(
|
||||
readFile("dist/client/.well-known/security.txt", "utf8")
|
||||
).resolves.toBe(security);
|
||||
await expect(readFile("dist/client/empty.txt", "utf8")).resolves.toBe("");
|
||||
await expect(readFile("dist/client/index.html", "utf8")).resolves.toContain(
|
||||
"<!DOCTYPE html>"
|
||||
);
|
||||
}, 30_000);
|
||||
|
||||
test("generates html, xml, and text document routes", async () => {
|
||||
await writeSiteData(
|
||||
createSiteData({
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
export const onBeforePrerenderStart = () => __PRERENDER_PATHS__;
|
||||
File renamed without changes.
@@ -0,0 +1,5 @@
|
||||
import type { Config } from "vike/types";
|
||||
|
||||
export default {
|
||||
clientHooks: false,
|
||||
} satisfies Config;
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { OnRenderHtmlAsync } from "vike/types";
|
||||
|
||||
export const onRenderHtml: OnRenderHtmlAsync = async (pageContext) => {
|
||||
pageContext.content = pageContext.data.pageMeta.content ?? "";
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
export default __PAGE_PATH__;
|
||||
@@ -3,7 +3,7 @@
|
||||
"private": true,
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"build": "vite build && vike prerender",
|
||||
"build": "vike build && vike prerender",
|
||||
"dev": "vite dev",
|
||||
"start": "vite preview",
|
||||
"typecheck": "tsc --noEmit"
|
||||
@@ -20,14 +20,14 @@
|
||||
"react": "18.3.0-canary-14898b6a9-20240318",
|
||||
"react-dom": "18.3.0-canary-14898b6a9-20240318",
|
||||
"shiki": "4.4.1",
|
||||
"vike": "^0.4.229"
|
||||
"vike": "0.4.267-commit-feffb0f"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^18.2.70",
|
||||
"@types/react-dom": "^18.2.25",
|
||||
"@vitejs/plugin-react": "^4.4.1",
|
||||
"@vitejs/plugin-react": "^4.7.0",
|
||||
"typescript": "7.0.2",
|
||||
"vite": "^6.3.4"
|
||||
"vite": "7.1.12"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.12.0"
|
||||
|
||||
Generated
+943
-172
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user