From b4e313262dffdb120f1c3734ac613c97a989d406 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Mon, 7 Aug 2023 13:40:25 +0300 Subject: [PATCH 01/12] Started working on new plugin remake --- README.md | 22 ++++++++++++---------- deno.json | 3 +++ src/mod.ts | 41 ++++++++++++++++++++++++++++++++++++++++- src/sitemap.ts | 2 +- 4 files changed, 56 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 525bd19..c28c52a 100644 --- a/README.md +++ b/README.md @@ -7,20 +7,22 @@ ## Getting Started -*Run the setup at the root of your project.* +*Import the plugin `freshSEOPlugin` in your Fresh app* -```shell -deno run --allow-read --allow-write https://deno.land/x/fresh_seo/init.ts - -``` - -
+```ts +import { start } from "$fresh/server.ts"; +import manifest from "./fresh.gen.ts"; -The following file should have been created: +import { freshSEOPlugin } from "https://deno.land/x/fresh_seo/mod.ts"; -`routes/sitemap.xml.ts` +await start(manifest, { + plugins: [ + // This line configures Fresh to use the first-party twind plugin. + freshSEOPlugin(manifest) + ], +}); -
+``` A basic sitemap should now be available at: diff --git a/deno.json b/deno.json index 910ec45..ee46808 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,8 @@ { "tasks": { "test": "deno test --allow-env --allow-write --allow-read" + }, + "imports": { + "$fresh/": "https://deno.land/x/fresh@1.3.1/" } } diff --git a/src/mod.ts b/src/mod.ts index 1203eb1..5fd170d 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1 +1,40 @@ -export { SitemapContext } from "./sitemap.ts"; +import { Manifest, Plugin } from "$fresh/server.ts"; +import { SitemapContext } from "./sitemap.ts"; + +interface PluginOptions { + include: string[]; + exclude: string[]; +} + +export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions): Plugin => { + return { + name: "fresh-seo", + routes: [ + { + path: "/sitemap.xml", + handler: (req) => { + const sitemap = new SitemapContext(req.url, manifest); + + if (opts.include) { + opts.include.forEach((path) => { + sitemap.add(path); + }) + } + + if (opts.exclude) { + opts.exclude.forEach((path) => { + sitemap.remove(path); + }) + } + + + return sitemap.render(); + } + } + ] + } +} + +export { + SitemapContext +}; diff --git a/src/sitemap.ts b/src/sitemap.ts index f5bc0f7..3b14490 100644 --- a/src/sitemap.ts +++ b/src/sitemap.ts @@ -130,4 +130,4 @@ function formatYearMonthDate(date: Date) { return `${date.getFullYear()}-${("00" + (date.getMonth() + 1)).slice(-2)}-${ ("00" + date.getDate()).slice(-2) }`; -} \ No newline at end of file +} From f9c988159a1a3f4f94df68106348539147b762c3 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Mon, 7 Aug 2023 14:45:21 +0300 Subject: [PATCH 02/12] Added ability to remove paths using glob matching Fixes #22 --- src/deps.ts | 1 + src/mod.ts | 2 +- src/sitemap.ts | 13 ++++++++++++- src/types.ts | 8 ++++++++ tests/sitemap.test.ts | 26 ++++++++++++++++++++++++++ 5 files changed, 48 insertions(+), 2 deletions(-) diff --git a/src/deps.ts b/src/deps.ts index d9ab978..2c5ace9 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -11,3 +11,4 @@ export { } from "https://deno.land/std@0.145.0/testing/asserts.ts"; export { FakeTime } from "https://deno.land/std@0.145.0/testing/time.ts"; export { ensureFile } from "https://deno.land/std@0.128.0/fs/mod.ts"; +export { filterFiles } from "https://deno.land/x/glob_filter@1.0.0/mod.ts"; diff --git a/src/mod.ts b/src/mod.ts index 5fd170d..75ddffe 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,5 +1,5 @@ -import { Manifest, Plugin } from "$fresh/server.ts"; import { SitemapContext } from "./sitemap.ts"; +import { Manifest, Plugin } from "./types.ts"; interface PluginOptions { include: string[]; diff --git a/src/sitemap.ts b/src/sitemap.ts index 3b14490..6f1f1f4 100644 --- a/src/sitemap.ts +++ b/src/sitemap.ts @@ -4,6 +4,7 @@ /// /// +import { filterFiles } from "https://deno.land/x/glob_filter@1.0.0/mod.ts"; import { basename, extname } from "./deps.ts"; import { type Manifest, type Route, type RouteProps } from "./types.ts"; @@ -54,22 +55,28 @@ export class SitemapContext { }); return this; } + const { changefreq, priority, lastmod } = props; + this.#routes.push({ pathName: route.replace(/(^\/?)|(\/?$)/, "/"), changefreq, priority, lastmod, }); + return this; } set(route: string, props?: RouteProps) { if (typeof props === "undefined") return this; + const i = this.#routes.findIndex( (v) => v.pathName === route.replace(/(^\/?)|(\/?$)/, "/"), ); + if (i === -1) return this; + const { changefreq, priority, lastmod } = props; const currentRoute = this.#routes[i]; this.#routes[i] = { @@ -82,7 +89,11 @@ export class SitemapContext { } remove(route: string) { - this.#routes = this.#routes.filter((r) => r.pathName !== route); + // glob_filter works best with absolute paths + // so we need to add the url to the route + const matching = filterFiles(this.#routes.map((r) => this.#url + r.pathName), { match: this.#url + route, ignore: this.#globalIgnore }) + this.#routes = this.#routes.filter((r) => !matching.map((m) => m.replace(this.#url, "")).includes(r.pathName)) + return this; } diff --git a/src/types.ts b/src/types.ts index 81b7309..47c05c5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,5 +1,13 @@ // deno-lint-ignore-file no-explicit-any +export interface Plugin { + name: string; + routes: { + path: string; + handler: (req: Request) => Response; + }[]; +} + export interface Manifest { routes: Record; islands: Record; diff --git a/tests/sitemap.test.ts b/tests/sitemap.test.ts index e0e3db2..08c875f 100644 --- a/tests/sitemap.test.ts +++ b/tests/sitemap.test.ts @@ -217,6 +217,32 @@ Deno.test("Remove certain routes", () => { assertStringIncludes(result, ""); }); +Deno.test("Remove all routes from /api", () => { + const manifest: Manifest = { + routes: { + "./routes/blog/[slug].tsx": { default: () => null }, + "./routes/api/[...rest].tsx": { default: () => null }, + }, + islands: {}, + baseUrl: url, + }; + const sitemap = new SitemapContext(url, manifest); + + const result = sitemap.remove("/api/*").generate(); + + assertStringIncludes(result, ''); + assertStringIncludes( + result, + '', + ); + + assertThrows(() => + assertStringIncludes(result, "https://deno.land/api") + ); + + assertStringIncludes(result, ""); +}); + Deno.test("Set current route", async (t) => { const manifest: Manifest = { routes: { From 55928c9da4fe8027a2751c030401b5f953e2302a Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Mon, 7 Aug 2023 14:48:09 +0300 Subject: [PATCH 03/12] Updated CI --- .github/workflows/test.yml | 4 +- deno.json | 3 -- deno.lock | 93 ++++++++++++++++++++++++++++++++++++++ src/deps.ts | 8 ++-- 4 files changed, 99 insertions(+), 9 deletions(-) create mode 100644 deno.lock diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 14ed004..4ec397d 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,6 +7,6 @@ jobs: - uses: actions/checkout@v3 - uses: denoland/setup-deno@v1 with: - deno-version: v1.26.X + deno-version: v1.34.X - - run: deno task test \ No newline at end of file + - run: deno task test diff --git a/deno.json b/deno.json index ee46808..910ec45 100644 --- a/deno.json +++ b/deno.json @@ -1,8 +1,5 @@ { "tasks": { "test": "deno test --allow-env --allow-write --allow-read" - }, - "imports": { - "$fresh/": "https://deno.land/x/fresh@1.3.1/" } } diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..27766d9 --- /dev/null +++ b/deno.lock @@ -0,0 +1,93 @@ +{ + "version": "2", + "remote": { + "https://deno.land/std@0.197.0/_util/diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", + "https://deno.land/std@0.197.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", + "https://deno.land/std@0.197.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", + "https://deno.land/std@0.197.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", + "https://deno.land/std@0.197.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", + "https://deno.land/std@0.197.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", + "https://deno.land/std@0.197.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", + "https://deno.land/std@0.197.0/assert/assert_equals.ts": "a0ee60574e437bcab2dcb79af9d48dc88845f8fd559468d9c21b15fd638ef943", + "https://deno.land/std@0.197.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", + "https://deno.land/std@0.197.0/assert/assert_false.ts": "a9962749f4bf5844e3fa494257f1de73d69e4fe0e82c34d0099287552163a2dc", + "https://deno.land/std@0.197.0/assert/assert_instance_of.ts": "09fd297352a5b5bbb16da2b5e1a0d8c6c44da5447772648622dcc7df7af1ddb8", + "https://deno.land/std@0.197.0/assert/assert_is_error.ts": "b4eae4e5d182272efc172bf28e2e30b86bb1650cd88aea059e5d2586d4160fb9", + "https://deno.land/std@0.197.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", + "https://deno.land/std@0.197.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", + "https://deno.land/std@0.197.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", + "https://deno.land/std@0.197.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", + "https://deno.land/std@0.197.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", + "https://deno.land/std@0.197.0/assert/assert_object_match.ts": "27439c4f41dce099317566144299468ca822f556f1cc697f4dc8ed61fe9fee4c", + "https://deno.land/std@0.197.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", + "https://deno.land/std@0.197.0/assert/assert_strict_equals.ts": "5cf29b38b3f8dece95287325723272aa04e04dbf158d886d662fa594fddc9ed3", + "https://deno.land/std@0.197.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", + "https://deno.land/std@0.197.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", + "https://deno.land/std@0.197.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", + "https://deno.land/std@0.197.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", + "https://deno.land/std@0.197.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", + "https://deno.land/std@0.197.0/assert/mod.ts": "08d55a652c22c5da0215054b21085cec25a5da47ce4a6f9de7d9ad36df35bdee", + "https://deno.land/std@0.197.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", + "https://deno.land/std@0.197.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", + "https://deno.land/std@0.197.0/async/delay.ts": "a6142eb44cdd856b645086af2b811b1fcce08ec06bb7d50969e6a872ee9b8659", + "https://deno.land/std@0.197.0/collections/_comparators.ts": "fa7f9a44cea1d270098a2a5a6f8bb30c61b595c1b1f983bd67c6297d766adffa", + "https://deno.land/std@0.197.0/collections/binary_search_node.ts": "8d99dd95901d73a0edbe105826ef7ce0e1111ce184d2d0410dbfda172c9ebf35", + "https://deno.land/std@0.197.0/collections/binary_search_tree.ts": "c3588493fd3b090453fade1903db19cbe398490db6a40a0574d7abdaa87ba652", + "https://deno.land/std@0.197.0/collections/red_black_node.ts": "eb766a69d82132fc4f1789eb3dc753781da7c3b0938756256be3764c9941e3ac", + "https://deno.land/std@0.197.0/collections/red_black_tree.ts": "e78d2aa89d23410079ad1f26e8bf95f05c84c8f8a9ef9c9ae41b312d7062b861", + "https://deno.land/std@0.197.0/fmt/colors.ts": "a7eecffdf3d1d54db890723b303847b6e0a1ab4b528ba6958b8f2e754cf1b3bc", + "https://deno.land/std@0.197.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978", + "https://deno.land/std@0.197.0/fs/copy.ts": "b4f7fe87190d7b310c88a2d9ff845210c0a2b7b0a094ec509747359023beb7d6", + "https://deno.land/std@0.197.0/fs/empty_dir.ts": "c3d2da4c7352fab1cf144a1ecfef58090769e8af633678e0f3fabaef98594688", + "https://deno.land/std@0.197.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", + "https://deno.land/std@0.197.0/fs/ensure_file.ts": "c38602670bfaf259d86ca824a94e6cb9e5eb73757fefa4ebf43a90dd017d53d9", + "https://deno.land/std@0.197.0/fs/ensure_link.ts": "c0f5b2f0ec094ed52b9128eccb1ee23362a617457aa0f699b145d4883f5b2fb4", + "https://deno.land/std@0.197.0/fs/ensure_symlink.ts": "5006ab2f458159c56d689b53b1e48d57e05eeb1eaf64e677f7f76a30bc4fdba1", + "https://deno.land/std@0.197.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", + "https://deno.land/std@0.197.0/fs/exists.ts": "29c26bca8584a22876be7cb8844f1b6c8fc35e9af514576b78f5c6884d7ed02d", + "https://deno.land/std@0.197.0/fs/expand_glob.ts": "3e427436f4b3768727bd7de84169f10db75fe50b32e6dde567b8ae558a8d857a", + "https://deno.land/std@0.197.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", + "https://deno.land/std@0.197.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", + "https://deno.land/std@0.197.0/fs/walk.ts": "21a3cc5ff39c38acc93575213f54d5f1d44c5c6614ed97603d171eb0bf56a565", + "https://deno.land/std@0.197.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", + "https://deno.land/std@0.197.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", + "https://deno.land/std@0.197.0/path/_util.ts": "d7abb1e0dea065f427b89156e28cdeb32b045870acdf865833ba808a73b576d0", + "https://deno.land/std@0.197.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", + "https://deno.land/std@0.197.0/path/glob.ts": "d479e0a695621c94d3fd7fe7abd4f9499caf32a8de13f25073451c6ef420a4e1", + "https://deno.land/std@0.197.0/path/mod.ts": "f065032a7189404fdac3ad1a1551a9ac84751d2f25c431e101787846c86c79ef", + "https://deno.land/std@0.197.0/path/posix.ts": "8b7c67ac338714b30c816079303d0285dd24af6b284f7ad63da5b27372a2c94d", + "https://deno.land/std@0.197.0/path/separator.ts": "0fb679739d0d1d7bf45b68dacfb4ec7563597a902edbaf3c59b50d5bcadd93b1", + "https://deno.land/std@0.197.0/path/win32.ts": "4fca292f8d116fd6d62f243b8a61bd3d6835a9f0ede762ba5c01afe7c3c0aa12", + "https://deno.land/std@0.197.0/testing/_time.ts": "fecaf6fc7277d240d11b0de2e93b1c93ebbb4a3a61f0cb0b1741f66f69a4d22b", + "https://deno.land/std@0.197.0/testing/asserts.ts": "b4e4b1359393aeff09e853e27901a982c685cb630df30426ed75496961931946", + "https://deno.land/std@0.197.0/testing/time.ts": "a46fbfd61e6f011f15a63c8078399b1f7fa848d2c0c526f253b0535f5c3e7f45", + "https://deno.land/std@0.71.0/_util/assert.ts": "e1f76e77c5ccb5a8e0dbbbe6cce3a56d2556c8cb5a9a8802fc9565af72462149", + "https://deno.land/std@0.71.0/path/_constants.ts": "aba480c4a2c098b6374fdd5951fea13ecc8aaaf8b8aa4dae1871baa50243d676", + "https://deno.land/std@0.71.0/path/_interface.ts": "67b276380d297a7cedc3c17f7a0bf122edcfc96a3e1f69de06f379d85ba0e2c0", + "https://deno.land/std@0.71.0/path/_util.ts": "f0fa012d40ae9b6acbef03908e534eb11e694de6470fb4d78ea4f38829e735ab", + "https://deno.land/std@0.71.0/path/common.ts": "e4ec66a7416d56f60331b66e27a8a4f08c7b1cf48e350271cb69754a01cf5c04", + "https://deno.land/std@0.71.0/path/glob.ts": "43cc45e8649a35a199c4106dfdf66206f46dfd8e2e626a746512c1a1376fde99", + "https://deno.land/std@0.71.0/path/mod.ts": "6de8885c2534757097818e302becd1cefcbc4c28ac022cc279e612ee04e8cfd1", + "https://deno.land/std@0.71.0/path/posix.ts": "40c387415fca91b3482214cf74880c415cda90b337bebd2c9d4b62d2097bc146", + "https://deno.land/std@0.71.0/path/separator.ts": "9dd15d46ff84a16e13554f56af7fee1f85f8d0f379efbbe60ac066a60561f036", + "https://deno.land/std@0.71.0/path/win32.ts": "9e200471f24fb560d22e74b238133cb75ebb57bead933de1cc5aefed4cda3346", + "https://deno.land/std@0.85.0/_util/assert.ts": "2f868145a042a11d5ad0a3c748dcf580add8a0dbc0e876eaa0026303a5488f58", + "https://deno.land/std@0.85.0/_util/os.ts": "e282950a0eaa96760c0cf11e7463e66babd15ec9157d4c9ed49cc0925686f6a7", + "https://deno.land/std@0.85.0/fmt/colors.ts": "d253f2367e5feebcf0f49676533949c09ea07a2d95fb74958f6f5c1c22fbec49", + "https://deno.land/std@0.85.0/fs/exists.ts": "b0d2e31654819cc2a8d37df45d6b14686c0cc1d802e9ff09e902a63e98b85a00", + "https://deno.land/std@0.85.0/path/_constants.ts": "1247fee4a79b70c89f23499691ef169b41b6ccf01887a0abd131009c5581b853", + "https://deno.land/std@0.85.0/path/_interface.ts": "1fa73b02aaa24867e481a48492b44f2598cd9dfa513c7b34001437007d3642e4", + "https://deno.land/std@0.85.0/path/_util.ts": "f4fa69aa3cbbd8568763bfc43c7236875015ba343602d8bafd332b4b4243681b", + "https://deno.land/std@0.85.0/path/common.ts": "eaf03d08b569e8a87e674e4e265e099f237472b6fd135b3cbeae5827035ea14a", + "https://deno.land/std@0.85.0/path/glob.ts": "4a524c1c9da3e79a9fdabdc6e850cd9e41bdf31e442856ffa19c5b123268ca95", + "https://deno.land/std@0.85.0/path/mod.ts": "4465dc494f271b02569edbb4a18d727063b5dbd6ed84283ff906260970a15d12", + "https://deno.land/std@0.85.0/path/posix.ts": "1408f8ba482a4dc5fc0a7cd7be28bbbff9608d2b3b5ffdcf288ae1228d959add", + "https://deno.land/std@0.85.0/path/separator.ts": "8fdcf289b1b76fd726a508f57d3370ca029ae6976fcde5044007f062e643ff1c", + "https://deno.land/std@0.85.0/path/win32.ts": "6ca052f54500f00cd7a5172fde62900626ab620dcd5bdcf4e6f5695d001ddef6", + "https://deno.land/x/glob_filter@1.0.0/mod.ts": "fd2e0a2729f800a6dd9e468500e24bf2c086f24ef63ee4245e1b50a7832c270d", + "https://deno.land/x/hackle@1.1.1/init.ts": "d058b0073b27ad0a1e7035af75a280c4a261d4b521b2c1a0129ef694c96b731c", + "https://deno.land/x/hackle@1.1.1/lib/get-date-format.ts": "9f117dd5e0925c188fe05da0dd4bda0bc3f97f62d43880bc853cc1c6526cb5e9", + "https://deno.land/x/hackle@1.1.1/mod.ts": "5848364fc9fa40cd8c64b3546ca6c94b56130af6b1aa44cabca3cf437481e930", + "https://deno.land/x/hackle@1.1.1/tools.ts": "ad92dd10a63d4d8022680d10b504848e5be0ebce81aa05ddc897af9baac9e1e1" + } +} diff --git a/src/deps.ts b/src/deps.ts index 2c5ace9..581a0be 100644 --- a/src/deps.ts +++ b/src/deps.ts @@ -3,12 +3,12 @@ export { extname, join, resolve, -} from "https://deno.land/std@0.153.0/path/mod.ts"; +} from "https://deno.land/std@0.197.0/path/mod.ts"; export { assert, assertStringIncludes, assertThrows, -} from "https://deno.land/std@0.145.0/testing/asserts.ts"; -export { FakeTime } from "https://deno.land/std@0.145.0/testing/time.ts"; -export { ensureFile } from "https://deno.land/std@0.128.0/fs/mod.ts"; +} from "https://deno.land/std@0.197.0/testing/asserts.ts"; +export { FakeTime } from "https://deno.land/std@0.197.0/testing/time.ts"; +export { ensureFile } from "https://deno.land/std@0.197.0/fs/mod.ts"; export { filterFiles } from "https://deno.land/x/glob_filter@1.0.0/mod.ts"; From 79a8266e6aa5fd3f26617e1bec2baa98ad210ad5 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Thu, 10 Aug 2023 13:32:58 +0300 Subject: [PATCH 04/12] Moved plugin to separate file --- src/mod.ts | 42 ++---------------------------------------- src/plugin.ts | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 40 deletions(-) create mode 100644 src/plugin.ts diff --git a/src/mod.ts b/src/mod.ts index 75ddffe..0258623 100644 --- a/src/mod.ts +++ b/src/mod.ts @@ -1,40 +1,2 @@ -import { SitemapContext } from "./sitemap.ts"; -import { Manifest, Plugin } from "./types.ts"; - -interface PluginOptions { - include: string[]; - exclude: string[]; -} - -export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions): Plugin => { - return { - name: "fresh-seo", - routes: [ - { - path: "/sitemap.xml", - handler: (req) => { - const sitemap = new SitemapContext(req.url, manifest); - - if (opts.include) { - opts.include.forEach((path) => { - sitemap.add(path); - }) - } - - if (opts.exclude) { - opts.exclude.forEach((path) => { - sitemap.remove(path); - }) - } - - - return sitemap.render(); - } - } - ] - } -} - -export { - SitemapContext -}; +export { SitemapContext } from "./sitemap.ts"; +export { freshSEOPlugin } from "./plugin.ts"; diff --git a/src/plugin.ts b/src/plugin.ts new file mode 100644 index 0000000..c4c9e0e --- /dev/null +++ b/src/plugin.ts @@ -0,0 +1,36 @@ +import { SitemapContext } from "./sitemap.ts"; +import { Manifest, Plugin } from "./types.ts"; + +interface PluginOptions { + include: string[]; + exclude: string[]; +} + +export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions): Plugin => { + return { + name: "fresh-seo", + routes: [ + { + path: "/sitemap.xml", + handler: (req) => { + const sitemap = new SitemapContext(req.url, manifest); + + if (opts.include) { + opts.include.forEach((path) => { + sitemap.add(path); + }) + } + + if (opts.exclude) { + opts.exclude.forEach((path) => { + sitemap.remove(path); + }) + } + + + return sitemap.render(); + } + } + ] + } +} From 722cadef8297d77c711cb792b522aabc6bff8f00 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Thu, 10 Aug 2023 13:37:02 +0300 Subject: [PATCH 05/12] Updated README --- README.md | 70 ++++++++++++++++++++++++------------------------------- 1 file changed, 30 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index c28c52a..244b389 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,3 @@ - # Fresh SEO πŸ‹β€„β€„β€„[![Badge License]][License] *Quickly creating sitemaps for your **Deno [Fresh project]**.* @@ -10,6 +9,7 @@ *Import the plugin `freshSEOPlugin` in your Fresh app* ```ts +// ./main.ts import { start } from "$fresh/server.ts"; import manifest from "./fresh.gen.ts"; @@ -17,7 +17,6 @@ import { freshSEOPlugin } from "https://deno.land/x/fresh_seo/mod.ts"; await start(manifest, { plugins: [ - // This line configures Fresh to use the first-party twind plugin. freshSEOPlugin(manifest) ], }); @@ -28,7 +27,6 @@ A basic sitemap should now be available at: [`http://localhost:8000/sitemap.xml`][Localhost] -

## How does it work? @@ -44,50 +42,42 @@ A basic sitemap should now be available at: *You will still have to map dynamic routes yourself!* ```ts -// ./routes/sitemap.xml.ts -import { SitemapContext } from 'https://deno.land/x/fresh_seo/mod.ts'; -import { Handlers } from '$fresh/server.ts'; -import manifest from '../fresh.gen.ts'; - -export const handler : Handlers = { - GET(request,context){ - const sitemap = new SitemapContext( - 'http://example.com', // put your domain here - manifest - ); - - // you can add additional page here - sitemap.add('/blog/hello-world'); - - return sitemap.render(); - } -} +// ./main.ts +import { start } from "$fresh/server.ts"; +import manifest from "./fresh.gen.ts"; + +import { freshSEOPlugin } from "https://deno.land/x/fresh_seo/mod.ts"; + +await start(manifest, { + plugins: [ + freshSEOPlugin(manifest, { + include: ["/blog/intro"] + }) + ], +}); ``` *You can also remove unwanted routes* ```ts -// ./routes/sitemap.xml.ts -import { SitemapContext } from 'https://deno.land/x/fresh_seo/mod.ts'; -import { Handlers } from '$fresh/server.ts'; -import manifest from '../fresh.gen.ts'; - -export const handler : Handlers = { - GET(request,context){ - const sitemap = new SitemapContext( - 'http://example.com', - manifest - ); - - // You can remove unwanted routes here - sitemap.remove('/admin'); - - return sitemap.render(); - } -} +// ./main.ts +import { start } from "$fresh/server.ts"; +import manifest from "./fresh.gen.ts"; + +import { freshSEOPlugin } from "https://deno.land/x/fresh_seo/mod.ts"; + +await start(manifest, { + plugins: [ + freshSEOPlugin(manifest, { + exclude: [ + "/blog/intro", + "/api/*" + ] + }) + ], +}); ``` -

## Testing From 6864332e3ba109ac7bfac1f55d161a695f8626a4 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Sat, 12 Aug 2023 01:09:37 +0300 Subject: [PATCH 06/12] Removed legacy way of creating setups --- init.ts | 3 -- src/initFile.ts | 89 ------------------------------------------------- 2 files changed, 92 deletions(-) delete mode 100644 init.ts delete mode 100644 src/initFile.ts diff --git a/init.ts b/init.ts deleted file mode 100644 index dbeb86a..0000000 --- a/init.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { init } from "./src/initFile.ts"; - -init(); diff --git a/src/initFile.ts b/src/initFile.ts deleted file mode 100644 index e3c2a18..0000000 --- a/src/initFile.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { ensureFile, join, resolve } from "./deps.ts"; - -function isUrl(input: string): boolean { - try { - new URL(input); - } catch (_) { - return false; - } - return true; -} - -function isValidUrl(inputUrl: string): boolean { - if (!isUrl(inputUrl.trim())) return false; - return true; -} - -function isValidDir(inputDir: string): boolean { - if (inputDir.trim().length === 0) return false; - return true; -} - -async function createSitemap(url: string) { - const routesDirectory = resolve("./routes"); - - const destination = join(routesDirectory, "sitemap.xml.ts"); - - await ensureFile(destination); - - const stub = `import { Handlers } from "$fresh/server.ts"; -import manifest from "../fresh.gen.ts"; -import { SitemapContext } from "https://deno.land/x/fresh_seo@0.2.1/mod.ts"; -export const handler: Handlers = { - GET(req, ctx) { - const sitemap = new SitemapContext("${url}", manifest); - // You can add additional page here - return sitemap.render(); - }, -}; -`; - - return Deno.writeTextFile(destination, stub); -} - -async function createRobotTxt( - url: string, - staticPath: string, -) { - const directory = resolve(staticPath); - - const destination = join(directory, "robots.txt"); - - await ensureFile(destination); - - const stub = `User-agent: * -Allow: / -Disallow: /api/* -Sitemap: ${url}/sitemap.xml -`; - - return Deno.writeTextFile(destination, stub); -} - -export async function init() { - let url = prompt( - "Please input your site's url (skip by enter):", - "http://example.com", - ); - - if (!url || !isValidUrl(url)) { - console.log( - "Invalid url input! Setting to default value:'http://example.com'", - ); - url = "http://example.com"; - } - - let staticPath = prompt( - "Please input your site's static folder path (skip by enter):", - "./static", - ); - - if (!staticPath || !isValidDir(staticPath)) { - console.log("Invalid folder input! Setting to default value:'./static'"); - staticPath = "./static"; - } - - await createSitemap(url); - - await createRobotTxt(url, staticPath); -} From 22b27baa815178ee97180a6cf3564ece7876037a Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Sat, 12 Aug 2023 01:10:38 +0300 Subject: [PATCH 07/12] Update src/plugin.ts Co-authored-by: sinyo-matu --- src/plugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugin.ts b/src/plugin.ts index c4c9e0e..f503054 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -6,7 +6,7 @@ interface PluginOptions { exclude: string[]; } -export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions): Plugin => { +export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions = {}): Plugin => { return { name: "fresh-seo", routes: [ From 9e4aea1ac4b721f2188a8211a6cdede7ac2fad76 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Sat, 12 Aug 2023 01:11:26 +0300 Subject: [PATCH 08/12] Update src/plugin.ts Co-authored-by: sinyo-matu --- src/plugin.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugin.ts b/src/plugin.ts index f503054..adfd879 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -2,8 +2,8 @@ import { SitemapContext } from "./sitemap.ts"; import { Manifest, Plugin } from "./types.ts"; interface PluginOptions { - include: string[]; - exclude: string[]; + include?: string[]; + exclude?: string[]; } export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions = {}): Plugin => { From 230a4b58b0b0b697c305f125efcb0f60ae7c8577 Mon Sep 17 00:00:00 2001 From: Savin Angel-Mario Date: Sat, 12 Aug 2023 01:15:19 +0300 Subject: [PATCH 09/12] Added options in the include --- src/plugin.ts | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugin.ts b/src/plugin.ts index adfd879..8c364c6 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,9 +1,9 @@ import { SitemapContext } from "./sitemap.ts"; -import { Manifest, Plugin } from "./types.ts"; +import { Manifest, Plugin, RouteProps } from "./types.ts"; interface PluginOptions { - include?: string[]; - exclude?: string[]; + include?: Array + exclude?: Array } export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions = {}): Plugin => { @@ -16,8 +16,13 @@ export const freshSEOPlugin = (manifest: Manifest, opts: PluginOptions = {}): Pl const sitemap = new SitemapContext(req.url, manifest); if (opts.include) { - opts.include.forEach((path) => { - sitemap.add(path); + opts.include.forEach((route) => { + if (typeof route === "string") { + sitemap.add(route); + return; + } + + sitemap.add(route.path, route.options); }) } From 43510f1c894e5b2f924444d5b0c18652a79d1d3e Mon Sep 17 00:00:00 2001 From: sinyo-matu Date: Mon, 14 Aug 2023 01:04:50 +0900 Subject: [PATCH 10/12] init plugin-tests branch --- tests/deps.ts | 12 +++ tests/fixture/.gitignore | 6 ++ tests/fixture/README.md | 16 ++++ tests/fixture/components/Button.tsx | 12 +++ tests/fixture/deno.json | 29 ++++++ tests/fixture/dev.ts | 5 + tests/fixture/fresh.gen.ts | 26 ++++++ tests/fixture/islands/Counter.tsx | 16 ++++ tests/fixture/main.ts | 15 +++ tests/fixture/options.ts | 5 + tests/fixture/routes/_404.tsx | 28 ++++++ tests/fixture/routes/_app.tsx | 13 +++ tests/fixture/routes/api/joke.ts | 21 +++++ tests/fixture/routes/greet/[name].tsx | 5 + tests/fixture/routes/index.tsx | 31 +++++++ tests/fixture/static/favicon.ico | Bin 0 -> 22382 bytes tests/fixture/static/logo.svg | 6 ++ tests/fixture/static/styles.css | 126 ++++++++++++++++++++++++++ tests/plugin.test.ts | 15 +++ tests/test_utils.ts | 58 ++++++++++++ 20 files changed, 445 insertions(+) create mode 100644 tests/deps.ts create mode 100644 tests/fixture/.gitignore create mode 100644 tests/fixture/README.md create mode 100644 tests/fixture/components/Button.tsx create mode 100644 tests/fixture/deno.json create mode 100755 tests/fixture/dev.ts create mode 100644 tests/fixture/fresh.gen.ts create mode 100644 tests/fixture/islands/Counter.tsx create mode 100644 tests/fixture/main.ts create mode 100644 tests/fixture/options.ts create mode 100644 tests/fixture/routes/_404.tsx create mode 100644 tests/fixture/routes/_app.tsx create mode 100644 tests/fixture/routes/api/joke.ts create mode 100644 tests/fixture/routes/greet/[name].tsx create mode 100644 tests/fixture/routes/index.tsx create mode 100644 tests/fixture/static/favicon.ico create mode 100644 tests/fixture/static/logo.svg create mode 100644 tests/fixture/static/styles.css create mode 100644 tests/plugin.test.ts create mode 100644 tests/test_utils.ts diff --git a/tests/deps.ts b/tests/deps.ts new file mode 100644 index 0000000..8caeaf2 --- /dev/null +++ b/tests/deps.ts @@ -0,0 +1,12 @@ +/// +/// +/// +/// +/// + +export { TextLineStream } from "https://deno.land/std@0.193.0/streams/text_line_stream.ts"; +export { + assert, + assertStringIncludes, + assertThrows, +} from "https://deno.land/std@0.197.0/assert/mod.ts"; diff --git a/tests/fixture/.gitignore b/tests/fixture/.gitignore new file mode 100644 index 0000000..4e06ffc --- /dev/null +++ b/tests/fixture/.gitignore @@ -0,0 +1,6 @@ +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local diff --git a/tests/fixture/README.md b/tests/fixture/README.md new file mode 100644 index 0000000..ec0e33e --- /dev/null +++ b/tests/fixture/README.md @@ -0,0 +1,16 @@ +# Fresh project + +Your new Fresh project is ready to go. You can follow the Fresh "Getting +Started" guide here: https://fresh.deno.dev/docs/getting-started + +### Usage + +Make sure to install Deno: https://deno.land/manual/getting_started/installation + +Then start the project: + +``` +deno task start +``` + +This will watch the project directory and restart as necessary. diff --git a/tests/fixture/components/Button.tsx b/tests/fixture/components/Button.tsx new file mode 100644 index 0000000..f1b80a0 --- /dev/null +++ b/tests/fixture/components/Button.tsx @@ -0,0 +1,12 @@ +import { JSX } from "preact"; +import { IS_BROWSER } from "$fresh/runtime.ts"; + +export function Button(props: JSX.HTMLAttributes) { + return ( + +

{props.count}

+ + + ); +} diff --git a/tests/fixture/main.ts b/tests/fixture/main.ts new file mode 100644 index 0000000..8e60257 --- /dev/null +++ b/tests/fixture/main.ts @@ -0,0 +1,15 @@ +/// +/// +/// +/// +/// + +import "$std/dotenv/load.ts"; + +import { start } from "$fresh/server.ts"; +import manifest from "./fresh.gen.ts"; +import { freshSEOPlugin } from "fresh-seo"; + +await start(manifest, { + plugins: [freshSEOPlugin(manifest)], +}); diff --git a/tests/fixture/options.ts b/tests/fixture/options.ts new file mode 100644 index 0000000..6daf8a1 --- /dev/null +++ b/tests/fixture/options.ts @@ -0,0 +1,5 @@ +import { freshSEOPlugin } from "fresh-seo"; +import manifest from "./fresh.gen.ts"; +export const options = { + plugins: [freshSEOPlugin(manifest)], +}; diff --git a/tests/fixture/routes/_404.tsx b/tests/fixture/routes/_404.tsx new file mode 100644 index 0000000..c8228ab --- /dev/null +++ b/tests/fixture/routes/_404.tsx @@ -0,0 +1,28 @@ + +import { Head } from "$fresh/runtime.ts"; + +export default function Error404() { + return ( + <> + + 404 - Page not found + +
+
+ the fresh logo: a sliced lemon dripping with juice +

404 - Page not found

+

+ The page you were looking for doesn't exist. +

+ Go back home +
+
+ + ); +} diff --git a/tests/fixture/routes/_app.tsx b/tests/fixture/routes/_app.tsx new file mode 100644 index 0000000..13789b6 --- /dev/null +++ b/tests/fixture/routes/_app.tsx @@ -0,0 +1,13 @@ +import { AppProps } from "$fresh/server.ts"; +import { Head } from "$fresh/runtime.ts"; + +export default function App({ Component }: AppProps) { + return ( + <> + + + + + + ); +} diff --git a/tests/fixture/routes/api/joke.ts b/tests/fixture/routes/api/joke.ts new file mode 100644 index 0000000..a3f4243 --- /dev/null +++ b/tests/fixture/routes/api/joke.ts @@ -0,0 +1,21 @@ +import { HandlerContext } from "$fresh/server.ts"; + +// Jokes courtesy of https://punsandoneliners.com/randomness/programmer-jokes/ +const JOKES = [ + "Why do Java developers often wear glasses? They can't C#.", + "A SQL query walks into a bar, goes up to two tables and says β€œcan I join you?”", + "Wasn't hard to crack Forrest Gump's password. 1forrest1.", + "I love pressing the F5 key. It's refreshing.", + "Called IT support and a chap from Australia came to fix my network connection. I asked β€œDo you come from a LAN down under?”", + "There are 10 types of people in the world. Those who understand binary and those who don't.", + "Why are assembly programmers often wet? They work below C level.", + "My favourite computer based band is the Black IPs.", + "What programme do you use to predict the music tastes of former US presidential candidates? An Al Gore Rhythm.", + "An SEO expert walked into a bar, pub, inn, tavern, hostelry, public house.", +]; + +export const handler = (_req: Request, _ctx: HandlerContext): Response => { + const randomIndex = Math.floor(Math.random() * JOKES.length); + const body = JOKES[randomIndex]; + return new Response(body); +}; diff --git a/tests/fixture/routes/greet/[name].tsx b/tests/fixture/routes/greet/[name].tsx new file mode 100644 index 0000000..9c06827 --- /dev/null +++ b/tests/fixture/routes/greet/[name].tsx @@ -0,0 +1,5 @@ +import { PageProps } from "$fresh/server.ts"; + +export default function Greet(props: PageProps) { + return
Hello {props.params.name}
; +} diff --git a/tests/fixture/routes/index.tsx b/tests/fixture/routes/index.tsx new file mode 100644 index 0000000..23b87e8 --- /dev/null +++ b/tests/fixture/routes/index.tsx @@ -0,0 +1,31 @@ +import { Head } from "$fresh/runtime.ts"; +import { useSignal } from "@preact/signals"; +import Counter from "../islands/Counter.tsx"; + +export default function Home() { + const count = useSignal(3); + return ( + <> + + fixture + +
+
+ the fresh logo: a sliced lemon dripping with juice +

Welcome to fresh

+

+ Try updating this message in the + ./routes/index.tsx file, and refresh. +

+ +
+
+ + ); +} diff --git a/tests/fixture/static/favicon.ico b/tests/fixture/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..1cfaaa2193b0f210107a559f7421569f57a25388 GIT binary patch literal 22382 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 bck@HGWY%2idMme??%p8ZW3z(%VE+9-Ofn0d literal 0 HcmV?d00001 diff --git a/tests/fixture/static/logo.svg b/tests/fixture/static/logo.svg new file mode 100644 index 0000000..ef2fbe4 --- /dev/null +++ b/tests/fixture/static/logo.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/tests/fixture/static/styles.css b/tests/fixture/static/styles.css new file mode 100644 index 0000000..c644131 --- /dev/null +++ b/tests/fixture/static/styles.css @@ -0,0 +1,126 @@ + +*, +*::before, +*::after { + box-sizing: boder-box; +} +* { + margin: 0; +} +button { + color: inherit; +} +button, [role="button"] { + cursor: pointer; +} +code { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, + "Liberation Mono", "Courier New", monospace; + font-size: 1em; +} +img, +svg { + display: block; +} +img, +video { + max-width: 100%; + height: auto; +} + +html { + line-height: 1.5; + -webkit-text-size-adjust: 100%; + font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} +.transition-colors { + transition-property: background-color, border-color, color, fill, stroke; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 150ms; +} +.my-6 { + margin-bottom: 1.5rem; + margin-top: 1.5rem; +} +.text-4xl { + font-size: 2.25rem; + line-height: 2.5rem; +} +.mx-2 { + margin-left: 0.5rem; + margin-right: 0.5rem; +} +.my-4 { + margin-bottom: 1rem; + margin-top: 1rem; +} +.mx-auto { + margin-left: auto; + margin-right: auto; +} +.px-4 { + padding-left: 1rem; + padding-right: 1rem; +} +.py-8 { + padding-bottom: 2rem; + padding-top: 2rem; +} +.bg-\[\#86efac\] { + background-color: #86efac; +} +.text-3xl { + font-size: 1.875rem; + line-height: 2.25rem; +} +.py-6 { + padding-bottom: 1.5rem; + padding-top: 1.5rem; +} +.px-2 { + padding-left: 0.5rem; + padding-right: 0.5rem; +} +.py-1 { + padding-bottom: 0.25rem; + padding-top: 0.25rem; +} +.border-gray-500 { + border-color: #6b7280; +} +.bg-white { + background-color: #fff; +} +.flex { + display: flex; +} +.gap-8 { + grid-gap: 2rem; + gap: 2rem; +} +.font-bold { + font-weight: 700; +} +.max-w-screen-md { + max-width: 768px; +} +.flex-col { + flex-direction: column; +} +.items-center { + align-items: center; +} +.justify-center { + justify-content: center; +} +.border-2 { + border-width: 2px; +} +.rounded { + border-radius: 0.25rem; +} +.hover:bg-gray-200:hover { + background-color: #e5e7eb; +} diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts new file mode 100644 index 0000000..0b5e46b --- /dev/null +++ b/tests/plugin.test.ts @@ -0,0 +1,15 @@ +import { withFresh } from "./test_utils.ts"; +import { assert, assertStringIncludes, assertThrows } from "./deps.ts"; + +Deno.test("/test plugin", async () => { + await withFresh("./tests/fixture/main.ts", async (address) => { + const resp = await fetch(`${address}/sitemap.xml`); + const xml = await resp.text(); + console.log(xml); + assertStringIncludes(xml, ''); + assertStringIncludes( + xml, + '' + ); + }); +}); diff --git a/tests/test_utils.ts b/tests/test_utils.ts new file mode 100644 index 0000000..8001b03 --- /dev/null +++ b/tests/test_utils.ts @@ -0,0 +1,58 @@ +import { TextLineStream } from "./deps.ts"; + +export async function startFreshServer(options: Deno.CommandOptions) { + const { serverProcess, lines, address } = await spawnServer(options); + + if (!address) { + throw new Error("Server didn't start up"); + } + + return { serverProcess, lines, address }; +} + +export async function withFresh( + name: string, + fn: (address: string) => Promise +) { + const { lines, serverProcess, address } = await startFreshServer({ + args: ["run", "-A", name], + }); + + try { + await fn(address); + } finally { + await lines.cancel(); + + serverProcess.kill("SIGTERM"); + + // Wait until the process exits + await serverProcess.status; + } +} + +async function spawnServer(options: Deno.CommandOptions, expectErrors = false) { + const serverProcess = new Deno.Command(Deno.execPath(), { + ...options, + stdin: "null", + stdout: "piped", + stderr: expectErrors ? "piped" : "inherit", + }).spawn(); + + const decoder = new TextDecoderStream(); + const lines: ReadableStream = serverProcess.stdout + .pipeThrough(decoder) + .pipeThrough(new TextLineStream(), { + preventCancel: true, + }); + + let address = ""; + for await (const line of lines) { + const match = line.match(/https?:\/\/localhost:\d+/g); + if (match) { + address = match[0]; + break; + } + } + + return { serverProcess, lines, address }; +} From 14b9c057535a47fa41a64e8f8e52c7fe3d2312fe Mon Sep 17 00:00:00 2001 From: sinyo-matu Date: Mon, 14 Aug 2023 13:03:33 +0900 Subject: [PATCH 11/12] add plugin tests --- deno.lock | 1 + tests/fixture/fresh.gen.ts | 24 ++++++++---- tests/fixture/routes/_500.tsx | 27 +++++++++++++ tests/fixture/routes/_middleware.tsx | 15 ++++++++ tests/fixture/routes/about.tsx | 3 ++ tests/fixture/routes/sitemap.xml.tsx | 3 ++ tests/plugin.test.ts | 57 ++++++++++++++++++++++------ tests/test_utils.ts | 16 ++++++++ 8 files changed, 127 insertions(+), 19 deletions(-) create mode 100644 tests/fixture/routes/_500.tsx create mode 100644 tests/fixture/routes/_middleware.tsx create mode 100644 tests/fixture/routes/about.tsx create mode 100644 tests/fixture/routes/sitemap.xml.tsx diff --git a/deno.lock b/deno.lock index 27766d9..1b18944 100644 --- a/deno.lock +++ b/deno.lock @@ -1,6 +1,7 @@ { "version": "2", "remote": { + "https://deno.land/std@0.193.0/streams/text_line_stream.ts": "0f2c4b33a5fdb2476f2e060974cba1347cefe99a4af33c28a57524b1a34750fa", "https://deno.land/std@0.197.0/_util/diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", "https://deno.land/std@0.197.0/_util/os.ts": "d932f56d41e4f6a6093d56044e29ce637f8dcc43c5a90af43504a889cf1775e3", "https://deno.land/std@0.197.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", diff --git a/tests/fixture/fresh.gen.ts b/tests/fixture/fresh.gen.ts index f8ebdff..7795607 100644 --- a/tests/fixture/fresh.gen.ts +++ b/tests/fixture/fresh.gen.ts @@ -3,19 +3,27 @@ // This file is automatically updated during development when running `dev.ts`. import * as $0 from "./routes/_404.tsx"; -import * as $1 from "./routes/_app.tsx"; -import * as $2 from "./routes/api/joke.ts"; -import * as $3 from "./routes/greet/[name].tsx"; -import * as $4 from "./routes/index.tsx"; +import * as $1 from "./routes/_500.tsx"; +import * as $2 from "./routes/_app.tsx"; +import * as $3 from "./routes/_middleware.tsx"; +import * as $4 from "./routes/about.tsx"; +import * as $5 from "./routes/api/joke.ts"; +import * as $6 from "./routes/greet/[name].tsx"; +import * as $7 from "./routes/index.tsx"; +import * as $8 from "./routes/sitemap.xml.tsx"; import * as $$0 from "./islands/Counter.tsx"; const manifest = { routes: { "./routes/_404.tsx": $0, - "./routes/_app.tsx": $1, - "./routes/api/joke.ts": $2, - "./routes/greet/[name].tsx": $3, - "./routes/index.tsx": $4, + "./routes/_500.tsx": $1, + "./routes/_app.tsx": $2, + "./routes/_middleware.tsx": $3, + "./routes/about.tsx": $4, + "./routes/api/joke.ts": $5, + "./routes/greet/[name].tsx": $6, + "./routes/index.tsx": $7, + "./routes/sitemap.xml.tsx": $8, }, islands: { "./islands/Counter.tsx": $$0, diff --git a/tests/fixture/routes/_500.tsx b/tests/fixture/routes/_500.tsx new file mode 100644 index 0000000..413716c --- /dev/null +++ b/tests/fixture/routes/_500.tsx @@ -0,0 +1,27 @@ +import { Head } from "$fresh/runtime.ts"; + +export default function Error500() { + return ( + <> + + 500 - Internal Server Error + +
+
+ the fresh logo: a sliced lemon dripping with juice +

500 - Internal Server Error

+

Sorry! Something went wrong.

+ + Go back home + +
+
+ + ); +} diff --git a/tests/fixture/routes/_middleware.tsx b/tests/fixture/routes/_middleware.tsx new file mode 100644 index 0000000..5a84b5a --- /dev/null +++ b/tests/fixture/routes/_middleware.tsx @@ -0,0 +1,15 @@ +import { MiddlewareHandlerContext } from "$fresh/server.ts"; + +export const handler = [timing]; + +async function timing( + _req: Request, + ctx: MiddlewareHandlerContext +): Promise { + const start = performance.now(); + const res = await ctx.next(); + const end = performance.now(); + const dur = (end - start).toFixed(1); + res.headers.set("Server-Timing", `handler;dur=${dur}`); + return res; +} diff --git a/tests/fixture/routes/about.tsx b/tests/fixture/routes/about.tsx new file mode 100644 index 0000000..562f6a8 --- /dev/null +++ b/tests/fixture/routes/about.tsx @@ -0,0 +1,3 @@ +export default function About() { + return
This is the about page
; +} diff --git a/tests/fixture/routes/sitemap.xml.tsx b/tests/fixture/routes/sitemap.xml.tsx new file mode 100644 index 0000000..314262c --- /dev/null +++ b/tests/fixture/routes/sitemap.xml.tsx @@ -0,0 +1,3 @@ +export default function Sitemap() { + return
this is the sitemap route
; +} diff --git a/tests/plugin.test.ts b/tests/plugin.test.ts index 0b5e46b..0e935c4 100644 --- a/tests/plugin.test.ts +++ b/tests/plugin.test.ts @@ -1,15 +1,50 @@ -import { withFresh } from "./test_utils.ts"; +import { + fetchSitemapAsText, + formatYearMonthDate, + withFresh, +} from "./test_utils.ts"; import { assert, assertStringIncludes, assertThrows } from "./deps.ts"; -Deno.test("/test plugin", async () => { - await withFresh("./tests/fixture/main.ts", async (address) => { - const resp = await fetch(`${address}/sitemap.xml`); - const xml = await resp.text(); - console.log(xml); - assertStringIncludes(xml, ''); - assertStringIncludes( - xml, - '' - ); +Deno.test("sitemap generation works", async (t) => { + await withFresh("./tests/fixture/dev.ts", async (address) => { + const sitemap = await fetchSitemapAsText(`${address}/sitemap.xml`); + const today = formatYearMonthDate(new Date()); + await t.step("should have sitemap.xml route", () => { + assertStringIncludes(sitemap, ''); + assertStringIncludes( + sitemap, + '' + ); + }); + + await t.step("static routes should present", () => { + // index route should present + assertStringIncludes(sitemap, `${address}/`); + assertStringIncludes(sitemap, `${today}`); + assertStringIncludes(sitemap, `daily`); + assertStringIncludes(sitemap, `0.8`); + assertStringIncludes(sitemap, ""); + + // about route should present + assertStringIncludes(sitemap, `${address}/about`); + assertStringIncludes(sitemap, `${today}`); + assertStringIncludes(sitemap, `daily`); + assertStringIncludes(sitemap, `0.8`); + assertStringIncludes(sitemap, ""); + }); + + await t.step("Special routes should not present", () => { + assert(!sitemap.includes(`https://${address}/_404`)); + assert(!sitemap.includes(`https://${address}/_middleware`)); + assert(!sitemap.includes(`https://${address}/_500`)); + }); + + await t.step("Dynamic routes should not present", () => { + assert(!sitemap.includes(`https://${address}/greet/[name]`)); + }); + + await t.step("sitemap.xml route should not present", () => { + assert(!sitemap.includes(`https://${address}/sitemap.xml`)); + }); }); }); diff --git a/tests/test_utils.ts b/tests/test_utils.ts index 8001b03..9f7db6f 100644 --- a/tests/test_utils.ts +++ b/tests/test_utils.ts @@ -56,3 +56,19 @@ async function spawnServer(options: Deno.CommandOptions, expectErrors = false) { return { serverProcess, lines, address }; } + +/** + * Format date to YYYY-MM-DD + * @param date + * @returns formatted date + */ +export function formatYearMonthDate(date: Date) { + return `${date.getFullYear()}-${("00" + (date.getMonth() + 1)).slice(-2)}-${( + "00" + date.getDate() + ).slice(-2)}`; +} + +export async function fetchSitemapAsText(address: string) { + const resp = await fetch(address); + return resp.text(); +} From 003ef9bae2295e17d02f2ae3c772f7a8158b6c67 Mon Sep 17 00:00:00 2001 From: sinyo-matu Date: Mon, 14 Aug 2023 13:28:38 +0900 Subject: [PATCH 12/12] update test arg because test need --allow-run --allow-net permission --- deno.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deno.json b/deno.json index 910ec45..5b6e845 100644 --- a/deno.json +++ b/deno.json @@ -1,5 +1,5 @@ { "tasks": { - "test": "deno test --allow-env --allow-write --allow-read" + "test": "deno test --allow-env --allow-write --allow-read --allow-run --allow-net" } }