livemark 0.20.0 → 0.21.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.
@@ -0,0 +1,17 @@
1
+ import type { Plugin } from "vite";
2
+ import type { Config } from "../models/config.ts";
3
+ export interface RobotsOptions {
4
+ config: Config;
5
+ }
6
+ /** Emit a default `robots.txt` into the client build output, with a
7
+ * `Sitemap:` line pointing at the TanStack-Start-generated `sitemap.xml`
8
+ * when {@link Config.site} is set. Skipped when the consumer ships
9
+ * their own `.livemark/public/robots.txt` so the standard publicDir
10
+ * overlay wins — Vite copies the publicDir before writeBundle, so
11
+ * unconditional emitFile would silently overwrite the override. */
12
+ export declare function robots(opts: RobotsOptions): Plugin;
13
+ /** Render the body of a permissive default `robots.txt`. A `Sitemap:`
14
+ * line is appended only when `site` is set, because the directive
15
+ * requires a fully-qualified URL; `base` is included so the URL matches
16
+ * where the build (and the `sitemap.xml` inside it) is actually mounted. */
17
+ export declare function renderRobots(config: Pick<Config, "site" | "base">): string;
@@ -0,0 +1,40 @@
1
+ import { existsSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ /** Emit a default `robots.txt` into the client build output, with a
4
+ * `Sitemap:` line pointing at the TanStack-Start-generated `sitemap.xml`
5
+ * when {@link Config.site} is set. Skipped when the consumer ships
6
+ * their own `.livemark/public/robots.txt` so the standard publicDir
7
+ * overlay wins — Vite copies the publicDir before writeBundle, so
8
+ * unconditional emitFile would silently overwrite the override. */
9
+ export function robots(opts) {
10
+ const overrideFile = join(opts.config.root, ".livemark", "public", "robots.txt");
11
+ return {
12
+ name: "livemark:robots",
13
+ applyToEnvironment(env) {
14
+ return env.name === "client";
15
+ },
16
+ generateBundle() {
17
+ if (existsSync(overrideFile))
18
+ return;
19
+ this.emitFile({
20
+ type: "asset",
21
+ fileName: "robots.txt",
22
+ source: renderRobots(opts.config),
23
+ });
24
+ },
25
+ };
26
+ }
27
+ /** Render the body of a permissive default `robots.txt`. A `Sitemap:`
28
+ * line is appended only when `site` is set, because the directive
29
+ * requires a fully-qualified URL; `base` is included so the URL matches
30
+ * where the build (and the `sitemap.xml` inside it) is actually mounted. */
31
+ export function renderRobots(config) {
32
+ const lines = ["User-agent: *", "Allow: /"];
33
+ if (config.site) {
34
+ const host = config.site.replace(/\/+$/, "");
35
+ const base = config.base ?? "";
36
+ lines.push("", `Sitemap: ${host}${base}/sitemap.xml`);
37
+ }
38
+ return `${lines.join("\n")}\n`;
39
+ }
40
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zb3VyY2UvcGx1Z2lucy92aXRlLXJvYm90cy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3BDLE9BQU8sRUFBRSxJQUFJLEVBQUUsTUFBTSxXQUFXLENBQUE7QUFRaEM7Ozs7O29FQUtvRTtBQUNwRSxNQUFNLFVBQVUsTUFBTSxDQUFDLElBQW1CO0lBQ3hDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FDdkIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLEVBQ2hCLFdBQVcsRUFDWCxRQUFRLEVBQ1IsWUFBWSxDQUNiLENBQUE7SUFDRCxPQUFPO1FBQ0wsSUFBSSxFQUFFLGlCQUFpQjtRQUN2QixrQkFBa0IsQ0FBQyxHQUFHO1lBQ3BCLE9BQU8sR0FBRyxDQUFDLElBQUksS0FBSyxRQUFRLENBQUE7UUFDOUIsQ0FBQztRQUNELGNBQWM7WUFDWixJQUFJLFVBQVUsQ0FBQyxZQUFZLENBQUM7Z0JBQUUsT0FBTTtZQUNwQyxJQUFJLENBQUMsUUFBUSxDQUFDO2dCQUNaLElBQUksRUFBRSxPQUFPO2dCQUNiLFFBQVEsRUFBRSxZQUFZO2dCQUN0QixNQUFNLEVBQUUsWUFBWSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUM7YUFDbEMsQ0FBQyxDQUFBO1FBQ0osQ0FBQztLQUNGLENBQUE7QUFDSCxDQUFDO0FBRUQ7Ozs2RUFHNkU7QUFDN0UsTUFBTSxVQUFVLFlBQVksQ0FBQyxNQUFxQztJQUNoRSxNQUFNLEtBQUssR0FBRyxDQUFDLGVBQWUsRUFBRSxVQUFVLENBQUMsQ0FBQTtJQUMzQyxJQUFJLE1BQU0sQ0FBQyxJQUFJLEVBQUUsQ0FBQztRQUNoQixNQUFNLElBQUksR0FBRyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxNQUFNLEVBQUUsRUFBRSxDQUFDLENBQUE7UUFDNUMsTUFBTSxJQUFJLEdBQUcsTUFBTSxDQUFDLElBQUksSUFBSSxFQUFFLENBQUE7UUFDOUIsS0FBSyxDQUFDLElBQUksQ0FBQyxFQUFFLEVBQUUsWUFBWSxJQUFJLEdBQUcsSUFBSSxjQUFjLENBQUMsQ0FBQTtJQUN2RCxDQUFDO0lBQ0QsT0FBTyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQTtBQUNoQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgZXhpc3RzU3luYyB9IGZyb20gXCJub2RlOmZzXCJcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCJcbmltcG9ydCB0eXBlIHsgUGx1Z2luIH0gZnJvbSBcInZpdGVcIlxuaW1wb3J0IHR5cGUgeyBDb25maWcgfSBmcm9tIFwiLi4vbW9kZWxzL2NvbmZpZy50c1wiXG5cbmV4cG9ydCBpbnRlcmZhY2UgUm9ib3RzT3B0aW9ucyB7XG4gIGNvbmZpZzogQ29uZmlnXG59XG5cbi8qKiBFbWl0IGEgZGVmYXVsdCBgcm9ib3RzLnR4dGAgaW50byB0aGUgY2xpZW50IGJ1aWxkIG91dHB1dCwgd2l0aCBhXG4gKiAgYFNpdGVtYXA6YCBsaW5lIHBvaW50aW5nIGF0IHRoZSBUYW5TdGFjay1TdGFydC1nZW5lcmF0ZWQgYHNpdGVtYXAueG1sYFxuICogIHdoZW4ge0BsaW5rIENvbmZpZy5zaXRlfSBpcyBzZXQuIFNraXBwZWQgd2hlbiB0aGUgY29uc3VtZXIgc2hpcHNcbiAqICB0aGVpciBvd24gYC5saXZlbWFyay9wdWJsaWMvcm9ib3RzLnR4dGAgc28gdGhlIHN0YW5kYXJkIHB1YmxpY0RpclxuICogIG92ZXJsYXkgd2lucyDigJQgVml0ZSBjb3BpZXMgdGhlIHB1YmxpY0RpciBiZWZvcmUgd3JpdGVCdW5kbGUsIHNvXG4gKiAgdW5jb25kaXRpb25hbCBlbWl0RmlsZSB3b3VsZCBzaWxlbnRseSBvdmVyd3JpdGUgdGhlIG92ZXJyaWRlLiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHJvYm90cyhvcHRzOiBSb2JvdHNPcHRpb25zKTogUGx1Z2luIHtcbiAgY29uc3Qgb3ZlcnJpZGVGaWxlID0gam9pbihcbiAgICBvcHRzLmNvbmZpZy5yb290LFxuICAgIFwiLmxpdmVtYXJrXCIsXG4gICAgXCJwdWJsaWNcIixcbiAgICBcInJvYm90cy50eHRcIixcbiAgKVxuICByZXR1cm4ge1xuICAgIG5hbWU6IFwibGl2ZW1hcms6cm9ib3RzXCIsXG4gICAgYXBwbHlUb0Vudmlyb25tZW50KGVudikge1xuICAgICAgcmV0dXJuIGVudi5uYW1lID09PSBcImNsaWVudFwiXG4gICAgfSxcbiAgICBnZW5lcmF0ZUJ1bmRsZSgpIHtcbiAgICAgIGlmIChleGlzdHNTeW5jKG92ZXJyaWRlRmlsZSkpIHJldHVyblxuICAgICAgdGhpcy5lbWl0RmlsZSh7XG4gICAgICAgIHR5cGU6IFwiYXNzZXRcIixcbiAgICAgICAgZmlsZU5hbWU6IFwicm9ib3RzLnR4dFwiLFxuICAgICAgICBzb3VyY2U6IHJlbmRlclJvYm90cyhvcHRzLmNvbmZpZyksXG4gICAgICB9KVxuICAgIH0sXG4gIH1cbn1cblxuLyoqIFJlbmRlciB0aGUgYm9keSBvZiBhIHBlcm1pc3NpdmUgZGVmYXVsdCBgcm9ib3RzLnR4dGAuIEEgYFNpdGVtYXA6YFxuICogIGxpbmUgaXMgYXBwZW5kZWQgb25seSB3aGVuIGBzaXRlYCBpcyBzZXQsIGJlY2F1c2UgdGhlIGRpcmVjdGl2ZVxuICogIHJlcXVpcmVzIGEgZnVsbHktcXVhbGlmaWVkIFVSTDsgYGJhc2VgIGlzIGluY2x1ZGVkIHNvIHRoZSBVUkwgbWF0Y2hlc1xuICogIHdoZXJlIHRoZSBidWlsZCAoYW5kIHRoZSBgc2l0ZW1hcC54bWxgIGluc2lkZSBpdCkgaXMgYWN0dWFsbHkgbW91bnRlZC4gKi9cbmV4cG9ydCBmdW5jdGlvbiByZW5kZXJSb2JvdHMoY29uZmlnOiBQaWNrPENvbmZpZywgXCJzaXRlXCIgfCBcImJhc2VcIj4pIHtcbiAgY29uc3QgbGluZXMgPSBbXCJVc2VyLWFnZW50OiAqXCIsIFwiQWxsb3c6IC9cIl1cbiAgaWYgKGNvbmZpZy5zaXRlKSB7XG4gICAgY29uc3QgaG9zdCA9IGNvbmZpZy5zaXRlLnJlcGxhY2UoL1xcLyskLywgXCJcIilcbiAgICBjb25zdCBiYXNlID0gY29uZmlnLmJhc2UgPz8gXCJcIlxuICAgIGxpbmVzLnB1c2goXCJcIiwgYFNpdGVtYXA6ICR7aG9zdH0ke2Jhc2V9L3NpdGVtYXAueG1sYClcbiAgfVxuICByZXR1cm4gYCR7bGluZXMuam9pbihcIlxcblwiKX1cXG5gXG59XG4iXX0=
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ import { describe, expect, it } from "vite-plus/test";
2
+ import { renderRobots } from "./vite-robots.js";
3
+ describe("renderRobots", () => {
4
+ it("emits an allow-all body when site is not set", () => {
5
+ expect(renderRobots({})).toBe("User-agent: *\nAllow: /\n");
6
+ });
7
+ it("appends a Sitemap line when site is set", () => {
8
+ expect(renderRobots({ site: "https://livemark.dev" })).toBe("User-agent: *\nAllow: /\n\nSitemap: https://livemark.dev/sitemap.xml\n");
9
+ });
10
+ it("strips a trailing slash from site before composing the URL", () => {
11
+ expect(renderRobots({ site: "https://livemark.dev/" })).toBe("User-agent: *\nAllow: /\n\nSitemap: https://livemark.dev/sitemap.xml\n");
12
+ });
13
+ it("includes the base path so the URL matches the build mount", () => {
14
+ expect(renderRobots({ site: "https://user.github.io", base: "/repo" })).toBe("User-agent: *\nAllow: /\n\nSitemap: https://user.github.io/repo/sitemap.xml\n");
15
+ });
16
+ it("ignores base when site is not set", () => {
17
+ expect(renderRobots({ base: "/repo" })).toBe("User-agent: *\nAllow: /\n");
18
+ });
19
+ });
20
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS1yb2JvdHMudW5pdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NvdXJjZS9wbHVnaW5zL3ZpdGUtcm9ib3RzLnVuaXQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLEVBQUUsRUFBRSxFQUFFLE1BQU0sZ0JBQWdCLENBQUE7QUFDckQsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBRS9DLFFBQVEsQ0FBQyxjQUFjLEVBQUUsR0FBRyxFQUFFO0lBQzVCLEVBQUUsQ0FBQyw4Q0FBOEMsRUFBRSxHQUFHLEVBQUU7UUFDdEQsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzVELENBQUMsQ0FBQyxDQUFBO0lBRUYsRUFBRSxDQUFDLHlDQUF5QyxFQUFFLEdBQUcsRUFBRTtRQUNqRCxNQUFNLENBQUMsWUFBWSxDQUFDLEVBQUUsSUFBSSxFQUFFLHNCQUFzQixFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FDekQsd0VBQXdFLENBQ3pFLENBQUE7SUFDSCxDQUFDLENBQUMsQ0FBQTtJQUVGLEVBQUUsQ0FBQyw0REFBNEQsRUFBRSxHQUFHLEVBQUU7UUFDcEUsTUFBTSxDQUFDLFlBQVksQ0FBQyxFQUFFLElBQUksRUFBRSx1QkFBdUIsRUFBRSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQzFELHdFQUF3RSxDQUN6RSxDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsMkRBQTJELEVBQUUsR0FBRyxFQUFFO1FBQ25FLE1BQU0sQ0FDSixZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsd0JBQXdCLEVBQUUsSUFBSSxFQUFFLE9BQU8sRUFBRSxDQUFDLENBQ2hFLENBQUMsSUFBSSxDQUNKLCtFQUErRSxDQUNoRixDQUFBO0lBQ0gsQ0FBQyxDQUFDLENBQUE7SUFFRixFQUFFLENBQUMsbUNBQW1DLEVBQUUsR0FBRyxFQUFFO1FBQzNDLE1BQU0sQ0FBQyxZQUFZLENBQUMsRUFBRSxJQUFJLEVBQUUsT0FBTyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQywyQkFBMkIsQ0FBQyxDQUFBO0lBQzNFLENBQUMsQ0FBQyxDQUFBO0FBQ0osQ0FBQyxDQUFDLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBkZXNjcmliZSwgZXhwZWN0LCBpdCB9IGZyb20gXCJ2aXRlLXBsdXMvdGVzdFwiXG5pbXBvcnQgeyByZW5kZXJSb2JvdHMgfSBmcm9tIFwiLi92aXRlLXJvYm90cy50c1wiXG5cbmRlc2NyaWJlKFwicmVuZGVyUm9ib3RzXCIsICgpID0+IHtcbiAgaXQoXCJlbWl0cyBhbiBhbGxvdy1hbGwgYm9keSB3aGVuIHNpdGUgaXMgbm90IHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7fSkpLnRvQmUoXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cIilcbiAgfSlcblxuICBpdChcImFwcGVuZHMgYSBTaXRlbWFwIGxpbmUgd2hlbiBzaXRlIGlzIHNldFwiLCAoKSA9PiB7XG4gICAgZXhwZWN0KHJlbmRlclJvYm90cyh7IHNpdGU6IFwiaHR0cHM6Ly9saXZlbWFyay5kZXZcIiB9KSkudG9CZShcbiAgICAgIFwiVXNlci1hZ2VudDogKlxcbkFsbG93OiAvXFxuXFxuU2l0ZW1hcDogaHR0cHM6Ly9saXZlbWFyay5kZXYvc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJzdHJpcHMgYSB0cmFpbGluZyBzbGFzaCBmcm9tIHNpdGUgYmVmb3JlIGNvbXBvc2luZyB0aGUgVVJMXCIsICgpID0+IHtcbiAgICBleHBlY3QocmVuZGVyUm9ib3RzKHsgc2l0ZTogXCJodHRwczovL2xpdmVtYXJrLmRldi9cIiB9KSkudG9CZShcbiAgICAgIFwiVXNlci1hZ2VudDogKlxcbkFsbG93OiAvXFxuXFxuU2l0ZW1hcDogaHR0cHM6Ly9saXZlbWFyay5kZXYvc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJpbmNsdWRlcyB0aGUgYmFzZSBwYXRoIHNvIHRoZSBVUkwgbWF0Y2hlcyB0aGUgYnVpbGQgbW91bnRcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChcbiAgICAgIHJlbmRlclJvYm90cyh7IHNpdGU6IFwiaHR0cHM6Ly91c2VyLmdpdGh1Yi5pb1wiLCBiYXNlOiBcIi9yZXBvXCIgfSksXG4gICAgKS50b0JlKFxuICAgICAgXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cXG5TaXRlbWFwOiBodHRwczovL3VzZXIuZ2l0aHViLmlvL3JlcG8vc2l0ZW1hcC54bWxcXG5cIixcbiAgICApXG4gIH0pXG5cbiAgaXQoXCJpZ25vcmVzIGJhc2Ugd2hlbiBzaXRlIGlzIG5vdCBzZXRcIiwgKCkgPT4ge1xuICAgIGV4cGVjdChyZW5kZXJSb2JvdHMoeyBiYXNlOiBcIi9yZXBvXCIgfSkpLnRvQmUoXCJVc2VyLWFnZW50OiAqXFxuQWxsb3c6IC9cXG5cIilcbiAgfSlcbn0pXG4iXX0=
@@ -7,10 +7,12 @@ import { defineConfig } from "vite";
7
7
  import svgr from "vite-plugin-svgr";
8
8
  import { loadConfig } from "./actions/config/load.js";
9
9
  import { livemark } from "./plugins/vite-livemark.js";
10
+ import { robots } from "./plugins/vite-robots.js";
10
11
  const config = await loadConfig();
11
12
  export default defineConfig({
12
13
  plugins: [
13
14
  livemark({ config }),
15
+ robots({ config }),
14
16
  devtools(),
15
17
  tailwind(),
16
18
  contentCollections({ configPath: "content-collections.ts" }),
@@ -41,4 +43,4 @@ export default defineConfig({
41
43
  svgr(),
42
44
  ],
43
45
  });
44
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxrQkFBa0IsTUFBTSwyQkFBMkIsQ0FBQTtBQUMxRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBQ2pFLE9BQU8sS0FBSyxNQUFNLHNCQUFzQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbkMsT0FBTyxJQUFJLE1BQU0sa0JBQWtCLENBQUE7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQTtBQUVyRCxNQUFNLE1BQU0sR0FBRyxNQUFNLFVBQVUsRUFBRSxDQUFBO0FBRWpDLGVBQWUsWUFBWSxDQUFDO0lBQzFCLE9BQU8sRUFBRTtRQUNQLFFBQVEsQ0FBQyxFQUFFLE1BQU0sRUFBRSxDQUFDO1FBQ3BCLFFBQVEsRUFBRTtRQUNWLFFBQVEsRUFBRTtRQUNWLGtCQUFrQixDQUFDLEVBQUUsVUFBVSxFQUFFLHdCQUF3QixFQUFFLENBQUM7UUFDNUQsYUFBYSxDQUFDO1lBQ1osWUFBWSxFQUFFLEdBQUc7WUFDakIsaUVBQWlFO1lBQ2pFLGdFQUFnRTtZQUNoRSx5REFBeUQ7WUFDekQsS0FBSyxFQUFFLENBQUMsRUFBRSxJQUFJLEVBQUUsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsR0FBRyxNQUFNLENBQUMsSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ3hELGdFQUFnRTtZQUNoRSxvRUFBb0U7WUFDcEUsa0VBQWtFO1lBQ2xFLDZCQUE2QjtZQUM3QixTQUFTLEVBQUU7Z0JBQ1QsT0FBTyxFQUFFLElBQUk7Z0JBQ2IsVUFBVSxFQUFFLElBQUk7Z0JBQ2hCLHdCQUF3QixFQUFFLEtBQUs7YUFDaEM7WUFDRCxPQUFPLEVBQUUsTUFBTSxDQUFDLElBQUk7Z0JBQ2xCLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLEVBQUU7Z0JBQ3RDLENBQUMsQ0FBQyxFQUFFLE9BQU8sRUFBRSxLQUFLLEVBQUU7WUFDdEIsTUFBTSxFQUFFO2dCQUNOLGtCQUFrQixFQUFFLGtCQUFrQjtnQkFDdEMsUUFBUSxFQUFFLE1BQU0sQ0FBQyxJQUFJO2FBQ3RCO1NBQ0YsQ0FBQztRQUNGLEtBQUssRUFBRTtRQUNQLElBQUksRUFBRTtLQUNQO0NBQ0YsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IGNvbnRlbnRDb2xsZWN0aW9ucyBmcm9tIFwiQGNvbnRlbnQtY29sbGVjdGlvbnMvdml0ZVwiXG5pbXBvcnQgdGFpbHdpbmQgZnJvbSBcIkB0YWlsd2luZGNzcy92aXRlXCJcbmltcG9ydCB7IGRldnRvb2xzIH0gZnJvbSBcIkB0YW5zdGFjay9kZXZ0b29scy12aXRlXCJcbmltcG9ydCB7IHRhbnN0YWNrU3RhcnQgfSBmcm9tIFwiQHRhbnN0YWNrL3JlYWN0LXN0YXJ0L3BsdWdpbi92aXRlXCJcbmltcG9ydCByZWFjdCBmcm9tIFwiQHZpdGVqcy9wbHVnaW4tcmVhY3RcIlxuaW1wb3J0IHsgZGVmaW5lQ29uZmlnIH0gZnJvbSBcInZpdGVcIlxuaW1wb3J0IHN2Z3IgZnJvbSBcInZpdGUtcGx1Z2luLXN2Z3JcIlxuaW1wb3J0IHsgbG9hZENvbmZpZyB9IGZyb20gXCIuL2FjdGlvbnMvY29uZmlnL2xvYWQudHNcIlxuaW1wb3J0IHsgbGl2ZW1hcmsgfSBmcm9tIFwiLi9wbHVnaW5zL3ZpdGUtbGl2ZW1hcmsudHNcIlxuXG5jb25zdCBjb25maWcgPSBhd2FpdCBsb2FkQ29uZmlnKClcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIGxpdmVtYXJrKHsgY29uZmlnIH0pLFxuICAgIGRldnRvb2xzKCksXG4gICAgdGFpbHdpbmQoKSxcbiAgICBjb250ZW50Q29sbGVjdGlvbnMoeyBjb25maWdQYXRoOiBcImNvbnRlbnQtY29sbGVjdGlvbnMudHNcIiB9KSxcbiAgICB0YW5zdGFja1N0YXJ0KHtcbiAgICAgIHNyY0RpcmVjdG9yeTogXCIuXCIsXG4gICAgICAvLyBTZWVkIHBhdGggbXVzdCBtYXRjaCB3aGF0IHRoZSBjcmF3bGVyIHByb2R1Y2VzIChyb3V0ZXItZW1pdHRlZFxuICAgICAgLy8gaHJlZnMgaW5jbHVkZSB0aGUgYmFzZXBhdGgpLCBvdGhlcndpc2UgcHJlcmVuZGVyIHJlY29yZHMgYm90aFxuICAgICAgLy8gc2hhcGVzIGFuZCB0aGUgc2l0ZW1hcCBlbmRzIHVwIHdpdGggZHVwbGljYXRlIGVudHJpZXMuXG4gICAgICBwYWdlczogW3sgcGF0aDogY29uZmlnLmJhc2UgPyBgJHtjb25maWcuYmFzZX0vYCA6IFwiL1wiIH1dLFxuICAgICAgLy8gRGlzYWJsZSByb3V0ZS10cmVlIGF1dG8tZGlzY292ZXJ5OiBpdCBwcm9kdWNlcyBiYXNlcGF0aC1uYWl2ZVxuICAgICAgLy8gcGF0aHMgKGUuZy4gYC9gKSB0aGF0IGR1cGxpY2F0ZSB0aGUgY3Jhd2wtZGVyaXZlZCBwcmVmaXhlZCBwYXRocyxcbiAgICAgIC8vIGJsb2F0aW5nIHBhZ2VzLmpzb24gYW5kIHNpdGVtYXAuIENyYXdsaW5nIGZyb20gdGhlIHNlZWQgYWxyZWFkeVxuICAgICAgLy8gcmVhY2hlcyBldmVyeSBsaW5rZWQgcGFnZS5cbiAgICAgIHByZXJlbmRlcjoge1xuICAgICAgICBlbmFibGVkOiB0cnVlLFxuICAgICAgICBjcmF3bExpbmtzOiB0cnVlLFxuICAgICAgICBhdXRvU3RhdGljUGF0aHNEaXNjb3Zlcnk6IGZhbHNlLFxuICAgICAgfSxcbiAgICAgIHNpdGVtYXA6IGNvbmZpZy5zaXRlXG4gICAgICAgID8geyBlbmFibGVkOiB0cnVlLCBob3N0OiBjb25maWcuc2l0ZSB9XG4gICAgICAgIDogeyBlbmFibGVkOiBmYWxzZSB9LFxuICAgICAgcm91dGVyOiB7XG4gICAgICAgIGdlbmVyYXRlZFJvdXRlVHJlZTogXCJyb3V0ZVRyZWUuZ2VuLnRzXCIsXG4gICAgICAgIGJhc2VwYXRoOiBjb25maWcuYmFzZSxcbiAgICAgIH0sXG4gICAgfSksXG4gICAgcmVhY3QoKSxcbiAgICBzdmdyKCksXG4gIF0sXG59KVxuIl19
46
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidml0ZS5jb25maWcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zb3VyY2Uvdml0ZS5jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxrQkFBa0IsTUFBTSwyQkFBMkIsQ0FBQTtBQUMxRCxPQUFPLFFBQVEsTUFBTSxtQkFBbUIsQ0FBQTtBQUN4QyxPQUFPLEVBQUUsUUFBUSxFQUFFLE1BQU0seUJBQXlCLENBQUE7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLG1DQUFtQyxDQUFBO0FBQ2pFLE9BQU8sS0FBSyxNQUFNLHNCQUFzQixDQUFBO0FBQ3hDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxNQUFNLENBQUE7QUFDbkMsT0FBTyxJQUFJLE1BQU0sa0JBQWtCLENBQUE7QUFDbkMsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLDBCQUEwQixDQUFBO0FBQ3JELE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sMEJBQTBCLENBQUE7QUFFakQsTUFBTSxNQUFNLEdBQUcsTUFBTSxVQUFVLEVBQUUsQ0FBQTtBQUVqQyxlQUFlLFlBQVksQ0FBQztJQUMxQixPQUFPLEVBQUU7UUFDUCxRQUFRLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUNwQixNQUFNLENBQUMsRUFBRSxNQUFNLEVBQUUsQ0FBQztRQUNsQixRQUFRLEVBQUU7UUFDVixRQUFRLEVBQUU7UUFDVixrQkFBa0IsQ0FBQyxFQUFFLFVBQVUsRUFBRSx3QkFBd0IsRUFBRSxDQUFDO1FBQzVELGFBQWEsQ0FBQztZQUNaLFlBQVksRUFBRSxHQUFHO1lBQ2pCLGlFQUFpRTtZQUNqRSxnRUFBZ0U7WUFDaEUseURBQXlEO1lBQ3pELEtBQUssRUFBRSxDQUFDLEVBQUUsSUFBSSxFQUFFLE1BQU0sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLEdBQUcsTUFBTSxDQUFDLElBQUksR0FBRyxDQUFDLENBQUMsQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUN4RCxnRUFBZ0U7WUFDaEUsb0VBQW9FO1lBQ3BFLGtFQUFrRTtZQUNsRSw2QkFBNkI7WUFDN0IsU0FBUyxFQUFFO2dCQUNULE9BQU8sRUFBRSxJQUFJO2dCQUNiLFVBQVUsRUFBRSxJQUFJO2dCQUNoQix3QkFBd0IsRUFBRSxLQUFLO2FBQ2hDO1lBQ0QsT0FBTyxFQUFFLE1BQU0sQ0FBQyxJQUFJO2dCQUNsQixDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxNQUFNLENBQUMsSUFBSSxFQUFFO2dCQUN0QyxDQUFDLENBQUMsRUFBRSxPQUFPLEVBQUUsS0FBSyxFQUFFO1lBQ3RCLE1BQU0sRUFBRTtnQkFDTixrQkFBa0IsRUFBRSxrQkFBa0I7Z0JBQ3RDLFFBQVEsRUFBRSxNQUFNLENBQUMsSUFBSTthQUN0QjtTQUNGLENBQUM7UUFDRixLQUFLLEVBQUU7UUFDUCxJQUFJLEVBQUU7S0FDUDtDQUNGLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCBjb250ZW50Q29sbGVjdGlvbnMgZnJvbSBcIkBjb250ZW50LWNvbGxlY3Rpb25zL3ZpdGVcIlxuaW1wb3J0IHRhaWx3aW5kIGZyb20gXCJAdGFpbHdpbmRjc3Mvdml0ZVwiXG5pbXBvcnQgeyBkZXZ0b29scyB9IGZyb20gXCJAdGFuc3RhY2svZGV2dG9vbHMtdml0ZVwiXG5pbXBvcnQgeyB0YW5zdGFja1N0YXJ0IH0gZnJvbSBcIkB0YW5zdGFjay9yZWFjdC1zdGFydC9wbHVnaW4vdml0ZVwiXG5pbXBvcnQgcmVhY3QgZnJvbSBcIkB2aXRlanMvcGx1Z2luLXJlYWN0XCJcbmltcG9ydCB7IGRlZmluZUNvbmZpZyB9IGZyb20gXCJ2aXRlXCJcbmltcG9ydCBzdmdyIGZyb20gXCJ2aXRlLXBsdWdpbi1zdmdyXCJcbmltcG9ydCB7IGxvYWRDb25maWcgfSBmcm9tIFwiLi9hY3Rpb25zL2NvbmZpZy9sb2FkLnRzXCJcbmltcG9ydCB7IGxpdmVtYXJrIH0gZnJvbSBcIi4vcGx1Z2lucy92aXRlLWxpdmVtYXJrLnRzXCJcbmltcG9ydCB7IHJvYm90cyB9IGZyb20gXCIuL3BsdWdpbnMvdml0ZS1yb2JvdHMudHNcIlxuXG5jb25zdCBjb25maWcgPSBhd2FpdCBsb2FkQ29uZmlnKClcblxuZXhwb3J0IGRlZmF1bHQgZGVmaW5lQ29uZmlnKHtcbiAgcGx1Z2luczogW1xuICAgIGxpdmVtYXJrKHsgY29uZmlnIH0pLFxuICAgIHJvYm90cyh7IGNvbmZpZyB9KSxcbiAgICBkZXZ0b29scygpLFxuICAgIHRhaWx3aW5kKCksXG4gICAgY29udGVudENvbGxlY3Rpb25zKHsgY29uZmlnUGF0aDogXCJjb250ZW50LWNvbGxlY3Rpb25zLnRzXCIgfSksXG4gICAgdGFuc3RhY2tTdGFydCh7XG4gICAgICBzcmNEaXJlY3Rvcnk6IFwiLlwiLFxuICAgICAgLy8gU2VlZCBwYXRoIG11c3QgbWF0Y2ggd2hhdCB0aGUgY3Jhd2xlciBwcm9kdWNlcyAocm91dGVyLWVtaXR0ZWRcbiAgICAgIC8vIGhyZWZzIGluY2x1ZGUgdGhlIGJhc2VwYXRoKSwgb3RoZXJ3aXNlIHByZXJlbmRlciByZWNvcmRzIGJvdGhcbiAgICAgIC8vIHNoYXBlcyBhbmQgdGhlIHNpdGVtYXAgZW5kcyB1cCB3aXRoIGR1cGxpY2F0ZSBlbnRyaWVzLlxuICAgICAgcGFnZXM6IFt7IHBhdGg6IGNvbmZpZy5iYXNlID8gYCR7Y29uZmlnLmJhc2V9L2AgOiBcIi9cIiB9XSxcbiAgICAgIC8vIERpc2FibGUgcm91dGUtdHJlZSBhdXRvLWRpc2NvdmVyeTogaXQgcHJvZHVjZXMgYmFzZXBhdGgtbmFpdmVcbiAgICAgIC8vIHBhdGhzIChlLmcuIGAvYCkgdGhhdCBkdXBsaWNhdGUgdGhlIGNyYXdsLWRlcml2ZWQgcHJlZml4ZWQgcGF0aHMsXG4gICAgICAvLyBibG9hdGluZyBwYWdlcy5qc29uIGFuZCBzaXRlbWFwLiBDcmF3bGluZyBmcm9tIHRoZSBzZWVkIGFscmVhZHlcbiAgICAgIC8vIHJlYWNoZXMgZXZlcnkgbGlua2VkIHBhZ2UuXG4gICAgICBwcmVyZW5kZXI6IHtcbiAgICAgICAgZW5hYmxlZDogdHJ1ZSxcbiAgICAgICAgY3Jhd2xMaW5rczogdHJ1ZSxcbiAgICAgICAgYXV0b1N0YXRpY1BhdGhzRGlzY292ZXJ5OiBmYWxzZSxcbiAgICAgIH0sXG4gICAgICBzaXRlbWFwOiBjb25maWcuc2l0ZVxuICAgICAgICA/IHsgZW5hYmxlZDogdHJ1ZSwgaG9zdDogY29uZmlnLnNpdGUgfVxuICAgICAgICA6IHsgZW5hYmxlZDogZmFsc2UgfSxcbiAgICAgIHJvdXRlcjoge1xuICAgICAgICBnZW5lcmF0ZWRSb3V0ZVRyZWU6IFwicm91dGVUcmVlLmdlbi50c1wiLFxuICAgICAgICBiYXNlcGF0aDogY29uZmlnLmJhc2UsXG4gICAgICB9LFxuICAgIH0pLFxuICAgIHJlYWN0KCksXG4gICAgc3ZncigpLFxuICBdLFxufSlcbiJdfQ==
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livemark",
3
- "version": "0.20.0",
3
+ "version": "0.21.0",
4
4
  "description": "Livemark description",
5
5
  "license": "MIT",
6
6
  "author": "Evgeny Karev",
@@ -33,8 +33,8 @@
33
33
  "@shikijs/twoslash": "^4.0.2",
34
34
  "@sindresorhus/slugify": "^3.0.0",
35
35
  "@tailwindcss/typography": "^0.5.19",
36
- "@tailwindcss/vite": "4.2.4",
37
- "@tanstack/devtools-vite": "0.6.0",
36
+ "@tailwindcss/vite": "4.3.0",
37
+ "@tanstack/devtools-vite": "0.7.0",
38
38
  "@tanstack/react-devtools": "0.10.0",
39
39
  "@tanstack/react-hotkeys": "^0.9.1",
40
40
  "@tanstack/react-query": "5.91.3",
@@ -87,7 +87,7 @@
87
87
  "unified": "^11.0.5",
88
88
  "unist-util-visit": "^5.1.0",
89
89
  "vaul": "1.1.2",
90
- "vite": "8.0.10",
90
+ "vite": "8.0.13",
91
91
  "vite-plugin-svgr": "5.2.0",
92
92
  "zod": "4.3.6"
93
93
  },
@@ -96,10 +96,8 @@
96
96
  "@semantic-release/exec": "7.1.0",
97
97
  "@types/node": "24.2.0",
98
98
  "@typescript/native-preview": "7.0.0-dev.20260316.1",
99
- "@vitest/coverage-v8": "4.1.5",
99
+ "@vitest/coverage-v8": "4.1.6",
100
100
  "conventional-changelog-conventionalcommits": "9.1.0",
101
- "husky": "9.1.7",
102
- "is-ci": "4.1.0",
103
101
  "lefthook": "2.1.1",
104
102
  "livemark": "link:.",
105
103
  "npm-check-updates": "18.0.1",
@@ -107,8 +105,7 @@
107
105
  "react-dom": "19.2.4",
108
106
  "semantic-release": "24.2.9",
109
107
  "tempy": "3.2.0",
110
- "vite-plus": "0.1.19",
111
- "vitest-polly": "2.1.0",
108
+ "vite-plus": "0.1.21",
112
109
  "wrangler": "4.85.0"
113
110
  },
114
111
  "peerDependencies": {
@@ -117,21 +114,23 @@
117
114
  },
118
115
  "engines": {
119
116
  "node": "^24.0.0",
120
- "pnpm": "^10.0.0"
117
+ "pnpm": "^11.0.0"
121
118
  },
122
119
  "scripts": {
123
120
  "build": "tsgo --project tsconfig.build.json",
124
- "bump": "ncu -u",
125
- "clean": "rm -rf pnpm-lock.yaml node_modules build",
121
+ "deps": "ncu -u",
126
122
  "docs:build": "node source/entrypoints/main.ts build",
127
123
  "docs:start": "node source/entrypoints/main.ts start",
128
124
  "format": "vp fmt",
125
+ "leaks": "gitleaks git --config=.gitleaks.toml --verbose --redact",
129
126
  "lint": "vp fmt --check && vp lint",
130
127
  "release": "pnpm publish --access public --ignore-scripts --no-git-checks",
131
- "setversion": "pnpm version --no-git-tag-version",
128
+ "scan": "pnpm leaks && pnpm vulns",
132
129
  "start": "node source/entrypoints/main.ts",
133
- "unit": "vp test",
134
130
  "test": "pnpm lint && pnpm type && pnpm unit",
135
- "type": "tsgo --noEmit"
131
+ "type": "tsgo --noEmit",
132
+ "unit": "vp test",
133
+ "vers": "pnpm version --no-git-tag-version",
134
+ "vulns": "semgrep --config=p/typescript --config=p/nodejs --config=p/owasp-top-ten --config=p/secrets --exclude=\"**/fixtures/**\" --metrics=off"
136
135
  }
137
136
  }
@@ -0,0 +1,51 @@
1
+ import { existsSync } from "node:fs"
2
+ import { join } from "node:path"
3
+ import type { Plugin } from "vite"
4
+ import type { Config } from "../models/config.ts"
5
+
6
+ export interface RobotsOptions {
7
+ config: Config
8
+ }
9
+
10
+ /** Emit a default `robots.txt` into the client build output, with a
11
+ * `Sitemap:` line pointing at the TanStack-Start-generated `sitemap.xml`
12
+ * when {@link Config.site} is set. Skipped when the consumer ships
13
+ * their own `.livemark/public/robots.txt` so the standard publicDir
14
+ * overlay wins — Vite copies the publicDir before writeBundle, so
15
+ * unconditional emitFile would silently overwrite the override. */
16
+ export function robots(opts: RobotsOptions): Plugin {
17
+ const overrideFile = join(
18
+ opts.config.root,
19
+ ".livemark",
20
+ "public",
21
+ "robots.txt",
22
+ )
23
+ return {
24
+ name: "livemark:robots",
25
+ applyToEnvironment(env) {
26
+ return env.name === "client"
27
+ },
28
+ generateBundle() {
29
+ if (existsSync(overrideFile)) return
30
+ this.emitFile({
31
+ type: "asset",
32
+ fileName: "robots.txt",
33
+ source: renderRobots(opts.config),
34
+ })
35
+ },
36
+ }
37
+ }
38
+
39
+ /** Render the body of a permissive default `robots.txt`. A `Sitemap:`
40
+ * line is appended only when `site` is set, because the directive
41
+ * requires a fully-qualified URL; `base` is included so the URL matches
42
+ * where the build (and the `sitemap.xml` inside it) is actually mounted. */
43
+ export function renderRobots(config: Pick<Config, "site" | "base">) {
44
+ const lines = ["User-agent: *", "Allow: /"]
45
+ if (config.site) {
46
+ const host = config.site.replace(/\/+$/, "")
47
+ const base = config.base ?? ""
48
+ lines.push("", `Sitemap: ${host}${base}/sitemap.xml`)
49
+ }
50
+ return `${lines.join("\n")}\n`
51
+ }
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vite-plus/test"
2
+ import { renderRobots } from "./vite-robots.ts"
3
+
4
+ describe("renderRobots", () => {
5
+ it("emits an allow-all body when site is not set", () => {
6
+ expect(renderRobots({})).toBe("User-agent: *\nAllow: /\n")
7
+ })
8
+
9
+ it("appends a Sitemap line when site is set", () => {
10
+ expect(renderRobots({ site: "https://livemark.dev" })).toBe(
11
+ "User-agent: *\nAllow: /\n\nSitemap: https://livemark.dev/sitemap.xml\n",
12
+ )
13
+ })
14
+
15
+ it("strips a trailing slash from site before composing the URL", () => {
16
+ expect(renderRobots({ site: "https://livemark.dev/" })).toBe(
17
+ "User-agent: *\nAllow: /\n\nSitemap: https://livemark.dev/sitemap.xml\n",
18
+ )
19
+ })
20
+
21
+ it("includes the base path so the URL matches the build mount", () => {
22
+ expect(
23
+ renderRobots({ site: "https://user.github.io", base: "/repo" }),
24
+ ).toBe(
25
+ "User-agent: *\nAllow: /\n\nSitemap: https://user.github.io/repo/sitemap.xml\n",
26
+ )
27
+ })
28
+
29
+ it("ignores base when site is not set", () => {
30
+ expect(renderRobots({ base: "/repo" })).toBe("User-agent: *\nAllow: /\n")
31
+ })
32
+ })
@@ -7,12 +7,14 @@ import { defineConfig } from "vite"
7
7
  import svgr from "vite-plugin-svgr"
8
8
  import { loadConfig } from "./actions/config/load.ts"
9
9
  import { livemark } from "./plugins/vite-livemark.ts"
10
+ import { robots } from "./plugins/vite-robots.ts"
10
11
 
11
12
  const config = await loadConfig()
12
13
 
13
14
  export default defineConfig({
14
15
  plugins: [
15
16
  livemark({ config }),
17
+ robots({ config }),
16
18
  devtools(),
17
19
  tailwind(),
18
20
  contentCollections({ configPath: "content-collections.ts" }),