From aa5d2548527f00ab8613047ffa4b6f1432ba177e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=97=8D+85CD?= <50108258+kwaa@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:06:00 +0800 Subject: [PATCH 1/4] feat: new plugin `robots` --- plugins/robots.ts | 96 ++ tests/__snapshots__/robots.test.ts.snap | 1811 +++++++++++++++++++++++ tests/robots.test.ts | 68 + 3 files changed, 1975 insertions(+) create mode 100644 plugins/robots.ts create mode 100644 tests/__snapshots__/robots.test.ts.snap create mode 100644 tests/robots.test.ts diff --git a/plugins/robots.ts b/plugins/robots.ts new file mode 100644 index 00000000..e5df4d78 --- /dev/null +++ b/plugins/robots.ts @@ -0,0 +1,96 @@ +import { merge } from "../core/utils/object.ts"; +import { Page } from "../core/file.ts"; + +import type Site from "../core/site.ts"; + +type Rule = { + /** User-agent */ + userAgent?: string; + /** Crawl-delay */ + crawlDelay?: string; + /** Disallow */ + disallow?: string; + /** Disavow */ + disavow?: string; + /** Allow */ + allow?: string; + /** Host */ + host?: string; + /** Sitemap */ + sitemap?: string; + /** Clean-param */ + cleanParam?: string; +}; + +const ruleSort = [ + "userAgent", + "crawlDelay", + "disallow", + "disavow", + "allow", + "host", + "sitemap", + "cleanParam", +]; + +export interface Options { + /** The robots.txt file name */ + filename: string; + allow?: string[]; + disallow?: string[]; + rules?: Rule[]; +} + +// Default options +export const defaults: Options = { + filename: "/robots.txt", + allow: ["*"], +}; + +/** A plugin to generate a robots.txt after build */ +export default (userOptions?: Partial) => { + const options: Options = merge(defaults, userOptions); + + return (site: Site) => { + site.addEventListener("beforeSave", async () => { + let rules: Rule[] = []; + + options.allow?.forEach((userAgent) => + rules.push({ + userAgent, + allow: "/", + }) + ); + + options.disallow?.forEach((userAgent) => + rules.push({ + userAgent, + disallow: "/", + }) + ); + + rules.push(...(options.rules ?? [])); + + // Create the robots.txt page + const robots = Page.create({ + url: options.filename, + content: rules + .map((rule) => + Object.entries(rule) + .sort(([keyA], [keyB]) => + ruleSort.indexOf(keyA) - ruleSort.indexOf(keyB) + ) + .map(([key, value]) => + `${key.charAt(0).toUpperCase()}${ + key.slice(1).replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase() + }: ${value}` + ) + .join("\n") + ).join("\n\n"), + }); + + // Add the robots.txt to pages + site.pages.push(robots); + }); + }; +}; diff --git a/tests/__snapshots__/robots.test.ts.snap b/tests/__snapshots__/robots.test.ts.snap new file mode 100644 index 00000000..b2e45deb --- /dev/null +++ b/tests/__snapshots__/robots.test.ts.snap @@ -0,0 +1,1811 @@ +export const snapshot = {}; + +snapshot[`Robots plugin 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + ], + src: [ + "/", + "/404.md", + "/_data.yml", + "/favicon.png", + "/page5.yaml", + "/pages", + "/pages/2020-06-21_page2.page.json", + "/pages/2021-01-02-18-32_page4.page.ts", + "/pages/_data", + "/pages/_data.yml", + "/pages/_data/colors.yml", + "/pages/_data/documents.ts", + "/pages/_data/drinks.js", + "/pages/_data/names.json", + "/pages/ghost", + "/pages/ghost/2021-12-29-page6.md", + "/pages/ghost/_data.yml", + "/pages/page1.md", + "/pages/page3.page.js", + "/pages/subpage", + "/pages/subpage/_data.yml", + "/pages/subpage/page7.page.js", + "/static.yml", + "/styles.css", + ], +} +`; + +snapshot[`Robots plugin 2`] = `[]`; + +snapshot[`Robots plugin 3`] = ` +[ + { + content: "User-agent: * +Allow: /", + data: { + basename: "robots", + content: "User-agent: * +Allow: /", + page: [ + "src", + "data", + ], + url: "/robots.txt", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: " +

This page is exported to /404.html, not /404/index.html

+", + data: { + basename: "404", + children: "

This page is exported to /404.html, not /404/index.html

+", + content: "This page is exported to \`/404.html\`, not \`/404/index.html\` +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/404.html", + }, + src: { + asset: false, + ext: ".md", + path: "/404", + remote: undefined, + }, + }, + { + content: " +Content of Page 5", + data: { + basename: "page5", + children: "Content of Page 5", + content: "Content of Page 5", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + title: "Page 5", + url: "/page5/", + }, + src: { + asset: false, + ext: ".yaml", + path: "/page5", + remote: undefined, + }, + }, + { + content: " +Content of Page 2", + data: { + basename: "page2", + children: "Content of Page 2", + colors: "Array(3)", + content: "Content of Page 2", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 2", + url: "/overrided-page2/", + }, + src: { + asset: false, + ext: ".page.json", + path: "/pages/2020-06-21_page2", + remote: undefined, + }, + }, + { + content: " +Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + data: { + basename: "page4", + children: "Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + colors: "Array(3)", + content: "default", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Overrided site name", + tags: "Array(1)", + title: "Page 4", + url: "/pages/page4/", + }, + src: { + asset: false, + ext: ".page.ts", + path: "/pages/2021-01-02-18-32_page4", + remote: undefined, + }, + }, + { + content: " +

Content of Page 6

+", + data: { + basename: "page6", + children: "

Content of Page 6

+", + colors: "Array(3)", + content: "Content of Page 6 +", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(2)", + title: "Page 6", + url: "/pages/page6/", + }, + src: { + asset: false, + ext: ".md", + path: "/pages/ghost/2021-12-29-page6", + remote: undefined, + }, + }, + { + content: "Content of Page 3", + data: { + basename: "page3", + colors: "Array(3)", + content: "Content of Page 3", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 3", + url: "/page_3", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/page3", + remote: undefined, + }, + }, + { + content: " +Content of Page 7", + data: { + basename: "page7", + children: "Content of Page 7", + colors: "Array(3)", + content: "Content of Page 7", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(3)", + url: "/pages/new-name/page7/", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/subpage/page7", + remote: undefined, + }, + }, +] +`; + +snapshot[`Robots plugin with allow 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + ], + src: [ + "/", + "/404.md", + "/_data.yml", + "/favicon.png", + "/page5.yaml", + "/pages", + "/pages/2020-06-21_page2.page.json", + "/pages/2021-01-02-18-32_page4.page.ts", + "/pages/_data", + "/pages/_data.yml", + "/pages/_data/colors.yml", + "/pages/_data/documents.ts", + "/pages/_data/drinks.js", + "/pages/_data/names.json", + "/pages/ghost", + "/pages/ghost/2021-12-29-page6.md", + "/pages/ghost/_data.yml", + "/pages/page1.md", + "/pages/page3.page.js", + "/pages/subpage", + "/pages/subpage/_data.yml", + "/pages/subpage/page7.page.js", + "/static.yml", + "/styles.css", + ], +} +`; + +snapshot[`Robots plugin with allow 2`] = `[]`; + +snapshot[`Robots plugin with allow 3`] = ` +[ + { + content: "User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: /", + data: { + basename: "robots", + content: "User-agent: Googlebot +Allow: / + +User-agent: Bingbot +Allow: /", + page: [ + "src", + "data", + ], + url: "/robots.txt", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: " +

This page is exported to /404.html, not /404/index.html

+", + data: { + basename: "404", + children: "

This page is exported to /404.html, not /404/index.html

+", + content: "This page is exported to \`/404.html\`, not \`/404/index.html\` +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/404.html", + }, + src: { + asset: false, + ext: ".md", + path: "/404", + remote: undefined, + }, + }, + { + content: " +Content of Page 5", + data: { + basename: "page5", + children: "Content of Page 5", + content: "Content of Page 5", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + title: "Page 5", + url: "/page5/", + }, + src: { + asset: false, + ext: ".yaml", + path: "/page5", + remote: undefined, + }, + }, + { + content: " +Content of Page 2", + data: { + basename: "page2", + children: "Content of Page 2", + colors: "Array(3)", + content: "Content of Page 2", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 2", + url: "/overrided-page2/", + }, + src: { + asset: false, + ext: ".page.json", + path: "/pages/2020-06-21_page2", + remote: undefined, + }, + }, + { + content: " +Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + data: { + basename: "page4", + children: "Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + colors: "Array(3)", + content: "default", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Overrided site name", + tags: "Array(1)", + title: "Page 4", + url: "/pages/page4/", + }, + src: { + asset: false, + ext: ".page.ts", + path: "/pages/2021-01-02-18-32_page4", + remote: undefined, + }, + }, + { + content: " +

Content of Page 6

+", + data: { + basename: "page6", + children: "

Content of Page 6

+", + colors: "Array(3)", + content: "Content of Page 6 +", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(2)", + title: "Page 6", + url: "/pages/page6/", + }, + src: { + asset: false, + ext: ".md", + path: "/pages/ghost/2021-12-29-page6", + remote: undefined, + }, + }, + { + content: "Content of Page 3", + data: { + basename: "page3", + colors: "Array(3)", + content: "Content of Page 3", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 3", + url: "/page_3", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/page3", + remote: undefined, + }, + }, + { + content: " +Content of Page 7", + data: { + basename: "page7", + children: "Content of Page 7", + colors: "Array(3)", + content: "Content of Page 7", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(3)", + url: "/pages/new-name/page7/", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/subpage/page7", + remote: undefined, + }, + }, +] +`; + +snapshot[`Robots plugin with disallow 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + ], + src: [ + "/", + "/404.md", + "/_data.yml", + "/favicon.png", + "/page5.yaml", + "/pages", + "/pages/2020-06-21_page2.page.json", + "/pages/2021-01-02-18-32_page4.page.ts", + "/pages/_data", + "/pages/_data.yml", + "/pages/_data/colors.yml", + "/pages/_data/documents.ts", + "/pages/_data/drinks.js", + "/pages/_data/names.json", + "/pages/ghost", + "/pages/ghost/2021-12-29-page6.md", + "/pages/ghost/_data.yml", + "/pages/page1.md", + "/pages/page3.page.js", + "/pages/subpage", + "/pages/subpage/_data.yml", + "/pages/subpage/page7.page.js", + "/static.yml", + "/styles.css", + ], +} +`; + +snapshot[`Robots plugin with disallow 2`] = `[]`; + +snapshot[`Robots plugin with disallow 3`] = ` +[ + { + content: "User-agent: * +Allow: / + +User-agent: ChatGPT-User +Disallow: /", + data: { + basename: "robots", + content: "User-agent: * +Allow: / + +User-agent: ChatGPT-User +Disallow: /", + page: [ + "src", + "data", + ], + url: "/robots.txt", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: " +

This page is exported to /404.html, not /404/index.html

+", + data: { + basename: "404", + children: "

This page is exported to /404.html, not /404/index.html

+", + content: "This page is exported to \`/404.html\`, not \`/404/index.html\` +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/404.html", + }, + src: { + asset: false, + ext: ".md", + path: "/404", + remote: undefined, + }, + }, + { + content: " +Content of Page 5", + data: { + basename: "page5", + children: "Content of Page 5", + content: "Content of Page 5", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + title: "Page 5", + url: "/page5/", + }, + src: { + asset: false, + ext: ".yaml", + path: "/page5", + remote: undefined, + }, + }, + { + content: " +Content of Page 2", + data: { + basename: "page2", + children: "Content of Page 2", + colors: "Array(3)", + content: "Content of Page 2", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 2", + url: "/overrided-page2/", + }, + src: { + asset: false, + ext: ".page.json", + path: "/pages/2020-06-21_page2", + remote: undefined, + }, + }, + { + content: " +Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + data: { + basename: "page4", + children: "Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + colors: "Array(3)", + content: "default", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Overrided site name", + tags: "Array(1)", + title: "Page 4", + url: "/pages/page4/", + }, + src: { + asset: false, + ext: ".page.ts", + path: "/pages/2021-01-02-18-32_page4", + remote: undefined, + }, + }, + { + content: " +

Content of Page 6

+", + data: { + basename: "page6", + children: "

Content of Page 6

+", + colors: "Array(3)", + content: "Content of Page 6 +", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(2)", + title: "Page 6", + url: "/pages/page6/", + }, + src: { + asset: false, + ext: ".md", + path: "/pages/ghost/2021-12-29-page6", + remote: undefined, + }, + }, + { + content: "Content of Page 3", + data: { + basename: "page3", + colors: "Array(3)", + content: "Content of Page 3", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 3", + url: "/page_3", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/page3", + remote: undefined, + }, + }, + { + content: " +Content of Page 7", + data: { + basename: "page7", + children: "Content of Page 7", + colors: "Array(3)", + content: "Content of Page 7", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(3)", + url: "/pages/new-name/page7/", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/subpage/page7", + remote: undefined, + }, + }, +] +`; + +snapshot[`Robots plugin with custom rules 1`] = ` +{ + formats: [ + { + engines: 0, + ext: ".page.toml", + loader: [AsyncFunction: toml], + pageType: "page", + }, + { + engines: 1, + ext: ".page.ts", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 1, + ext: ".page.js", + loader: [AsyncFunction: module], + pageType: "page", + }, + { + engines: 0, + ext: ".page.jsonc", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + engines: 0, + ext: ".page.json", + loader: [AsyncFunction: json], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".json", + loader: [AsyncFunction: json], + }, + { + dataLoader: [AsyncFunction: json], + engines: 0, + ext: ".jsonc", + loader: [AsyncFunction: json], + }, + { + engines: 1, + ext: ".md", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".markdown", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".js", + loader: [AsyncFunction: module], + }, + { + dataLoader: [AsyncFunction: module], + engines: 1, + ext: ".ts", + loader: [AsyncFunction: module], + }, + { + engines: 1, + ext: ".vento", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + engines: 1, + ext: ".vto", + loader: [AsyncFunction: text], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: toml], + engines: 0, + ext: ".toml", + loader: [AsyncFunction: toml], + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yaml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + { + dataLoader: [AsyncFunction: yaml], + engines: 0, + ext: ".yml", + loader: [AsyncFunction: yaml], + pageType: "page", + }, + ], + src: [ + "/", + "/404.md", + "/_data.yml", + "/favicon.png", + "/page5.yaml", + "/pages", + "/pages/2020-06-21_page2.page.json", + "/pages/2021-01-02-18-32_page4.page.ts", + "/pages/_data", + "/pages/_data.yml", + "/pages/_data/colors.yml", + "/pages/_data/documents.ts", + "/pages/_data/drinks.js", + "/pages/_data/names.json", + "/pages/ghost", + "/pages/ghost/2021-12-29-page6.md", + "/pages/ghost/_data.yml", + "/pages/page1.md", + "/pages/page3.page.js", + "/pages/subpage", + "/pages/subpage/_data.yml", + "/pages/subpage/page7.page.js", + "/static.yml", + "/styles.css", + ], +} +`; + +snapshot[`Robots plugin with custom rules 2`] = `[]`; + +snapshot[`Robots plugin with custom rules 3`] = ` +[ + { + content: "User-agent: * +Allow: / + +User-agent: * +Disallow: /admin + +Sitemap: https://example.com/sitemap.xml", + data: { + basename: "robots", + content: "User-agent: * +Allow: / + +User-agent: * +Disallow: /admin + +Sitemap: https://example.com/sitemap.xml", + page: [ + "src", + "data", + ], + url: "/robots.txt", + }, + src: { + asset: true, + ext: "", + path: "", + remote: undefined, + }, + }, + { + content: " +

This page is exported to /404.html, not /404/index.html

+", + data: { + basename: "404", + children: "

This page is exported to /404.html, not /404/index.html

+", + content: "This page is exported to \`/404.html\`, not \`/404/index.html\` +", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + url: "/404.html", + }, + src: { + asset: false, + ext: ".md", + path: "/404", + remote: undefined, + }, + }, + { + content: " +Content of Page 5", + data: { + basename: "page5", + children: "Content of Page 5", + content: "Content of Page 5", + date: [], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Default site name", + tags: "Array(0)", + title: "Page 5", + url: "/page5/", + }, + src: { + asset: false, + ext: ".yaml", + path: "/page5", + remote: undefined, + }, + }, + { + content: " +Content of Page 2", + data: { + basename: "page2", + children: "Content of Page 2", + colors: "Array(3)", + content: "Content of Page 2", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 2", + url: "/overrided-page2/", + }, + src: { + asset: false, + ext: ".page.json", + path: "/pages/2020-06-21_page2", + remote: undefined, + }, + }, + { + content: " +Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + data: { + basename: "page4", + children: "Content of Page 4 in Overrided site name, from the file /pages/2021-01-02-18-32_page4.page.ts", + colors: "Array(3)", + content: "default", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Overrided site name", + tags: "Array(1)", + title: "Page 4", + url: "/pages/page4/", + }, + src: { + asset: false, + ext: ".page.ts", + path: "/pages/2021-01-02-18-32_page4", + remote: undefined, + }, + }, + { + content: " +

Content of Page 6

+", + data: { + basename: "page6", + children: "

Content of Page 6

+", + colors: "Array(3)", + content: "Content of Page 6 +", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(2)", + title: "Page 6", + url: "/pages/page6/", + }, + src: { + asset: false, + ext: ".md", + path: "/pages/ghost/2021-12-29-page6", + remote: undefined, + }, + }, + { + content: "Content of Page 3", + data: { + basename: "page3", + colors: "Array(3)", + content: "Content of Page 3", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(1)", + title: "Page 3", + url: "/page_3", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/page3", + remote: undefined, + }, + }, + { + content: " +Content of Page 7", + data: { + basename: "page7", + children: "Content of Page 7", + colors: "Array(3)", + content: "Content of Page 7", + date: [], + documents: "Array(3)", + drinks: [ + "alcoholic", + "others", + ], + imagick: "Array(1)", + mergedKeys: [ + "tags", + "metas", + "imagick", + ], + metas: [ + "title", + "description", + ], + names: "Array(2)", + page: [ + "src", + "data", + ], + paginate: "paginate", + search: [], + site: "Folder overrided site name", + tags: "Array(3)", + url: "/pages/new-name/page7/", + }, + src: { + asset: false, + ext: ".page.js", + path: "/pages/subpage/page7", + remote: undefined, + }, + }, +] +`; diff --git a/tests/robots.test.ts b/tests/robots.test.ts new file mode 100644 index 00000000..17c4ebe8 --- /dev/null +++ b/tests/robots.test.ts @@ -0,0 +1,68 @@ +import { assertSiteSnapshot, build, getSite } from "./utils.ts"; +import robots from "../plugins/robots.ts"; + +Deno.test("Robots plugin", async (t) => { + const site = getSite({ + src: "normal", + location: new URL("https://example.com/"), + }); + + site.use(robots()); + site.ignore("static.yml"); + + await build(site); + await assertSiteSnapshot(t, site); +}); + +Deno.test("Robots plugin with allow", async (t) => { + const site = getSite({ + src: "normal", + location: new URL("https://example.com/"), + }); + + site.use(robots({ + allow: ["Googlebot", "Bingbot"], + })); + site.ignore("static.yml"); + + await build(site); + await assertSiteSnapshot(t, site); +}); + +Deno.test("Robots plugin with disallow", async (t) => { + const site = getSite({ + src: "normal", + location: new URL("https://example.com/"), + }); + + site.use(robots({ + disallow: ["ChatGPT-User"], + })); + site.ignore("static.yml"); + + await build(site); + await assertSiteSnapshot(t, site); +}); + +Deno.test("Robots plugin with custom rules", async (t) => { + const site = getSite({ + src: "normal", + location: new URL("https://example.com/"), + }); + + site.use(robots({ + rules: [ + { + userAgent: "*", + disallow: "/admin", + }, + { + sitemap: new URL("/sitemap.xml", site.options.location).href, + }, + ], + })); + site.ignore("static.yml"); + + await build(site); + await assertSiteSnapshot(t, site); +}); From 919bc2b1ddf8c7b87a08d42b68b11ac82ec582c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=97=8D+85CD?= <50108258+kwaa@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:08:57 +0800 Subject: [PATCH 2/4] chore: update changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b34c2206..6e9b7757 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ Go to the `v1` branch to see the changelog of Lume 1. - New plugin: `fff` [#529]. - New plugin: `redirects` [#534]. - New plugin: `og_images` [#534]. +- New plugin: `robots` [#570]. - New command `lume cms`. - `onDemand` plugin: support async extraData function. - `lume:*` global events. @@ -285,6 +286,7 @@ Go to the `v1` branch to see the changelog of Lume 1. [#566]: https://github.com/lumeland/lume/issues/566 [#567]: https://github.com/lumeland/lume/issues/567 [#569]: https://github.com/lumeland/lume/issues/569 +[#570]: https://github.com/lumeland/lume/issues/570 [2.1.0]: https://github.com/lumeland/lume/compare/v2.0.3...HEAD [2.0.3]: https://github.com/lumeland/lume/compare/v2.0.2...v2.0.3 From b2c148a2ab4125dad2fab231980488520cc546d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=97=8D+85CD?= <50108258+kwaa@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:09:47 +0800 Subject: [PATCH 3/4] fix: lint code --- plugins/robots.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/robots.ts b/plugins/robots.ts index e5df4d78..20639f92 100644 --- a/plugins/robots.ts +++ b/plugins/robots.ts @@ -52,8 +52,8 @@ export default (userOptions?: Partial) => { const options: Options = merge(defaults, userOptions); return (site: Site) => { - site.addEventListener("beforeSave", async () => { - let rules: Rule[] = []; + site.addEventListener("beforeSave", () => { + const rules: Rule[] = []; options.allow?.forEach((userAgent) => rules.push({ From 21c2258e7915d8c57e526c5ba0306f241b9a5b7b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=97=8D+85CD?= <50108258+kwaa@users.noreply.github.com> Date: Tue, 13 Feb 2024 23:12:52 +0800 Subject: [PATCH 4/4] fix: update plugins test --- core/utils/lume_config.ts | 1 + tests/plugins.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/core/utils/lume_config.ts b/core/utils/lume_config.ts index dc27eee5..68f0666c 100644 --- a/core/utils/lume_config.ts +++ b/core/utils/lume_config.ts @@ -37,6 +37,7 @@ export const pluginNames = [ "relative_urls", "remark", "resolve_urls", + "robots", "sass", "sheets", "sitemap", diff --git a/tests/plugins.test.ts b/tests/plugins.test.ts index 5af39065..7421a60c 100644 --- a/tests/plugins.test.ts +++ b/tests/plugins.test.ts @@ -44,6 +44,7 @@ Deno.test("Plugins list in init", () => { "relative_urls", "remark", "resolve_urls", + "robots", "sass", "sheets", "sitemap",