From c6d0233e33c4fa6686c3ccced0ece65939a063a1 Mon Sep 17 00:00:00 2001 From: Atila Fassina Date: Mon, 13 Mar 2023 15:57:17 +0100 Subject: [PATCH] feat(sitemap): support SSR generated routes --- packages/integrations/sitemap/package.json | 1 + packages/integrations/sitemap/src/index.ts | 71 ++++++++++++++++--- .../sitemap/src/utils/is-route-prerendered.ts | 8 +++ pnpm-lock.yaml | 2 + 4 files changed, 71 insertions(+), 11 deletions(-) create mode 100644 packages/integrations/sitemap/src/utils/is-route-prerendered.ts diff --git a/packages/integrations/sitemap/package.json b/packages/integrations/sitemap/package.json index 7d8c93d277660..4962033388459 100644 --- a/packages/integrations/sitemap/package.json +++ b/packages/integrations/sitemap/package.json @@ -33,6 +33,7 @@ "test": "mocha --timeout 20000" }, "dependencies": { + "fast-glob": "^3.2.11", "sitemap": "^7.1.1", "zod": "^3.17.3" }, diff --git a/packages/integrations/sitemap/src/index.ts b/packages/integrations/sitemap/src/index.ts index 4feee29587a41..2cc5c9dd3fe57 100644 --- a/packages/integrations/sitemap/src/index.ts +++ b/packages/integrations/sitemap/src/index.ts @@ -5,11 +5,14 @@ import { simpleSitemapAndIndex, SitemapItemLoose, } from 'sitemap'; +import { resolve } from 'path'; import { fileURLToPath } from 'url'; +import fg from 'fast-glob'; import { ZodError } from 'zod'; import { generateSitemap } from './generate-sitemap.js'; import { Logger } from './utils/logger.js'; +import { isRoutePrerendered } from './utils/is-route-prerendered.js'; import { validateOptions } from './validate-options.js'; export type ChangeFreq = `${EnumChangefreq}`; @@ -51,18 +54,71 @@ const OUTFILE = 'sitemap-index.xml'; const createPlugin = (options?: SitemapOptions): AstroIntegration => { let config: AstroConfig; + const logger = new Logger(PKG_NAME); + return { name: PKG_NAME, hooks: { 'astro:config:done': async ({ config: cfg }) => { - config = cfg; + if (cfg.site) { + config = cfg; + } else { + // eslint-disable-next-line no-console + console.warn( + 'The Sitemap integration requires the `site` astro.config option. Skipping.' + ); + return; + } }, - 'astro:build:done': async ({ dir, pages }) => { - const logger = new Logger(PKG_NAME); + 'astro:build:start': async () => { + if (config.output !== 'server' || !config.site) { + return; + } + + const srcPath = fileURLToPath(config.srcDir); + const pagesPath = resolve(srcPath, 'pages'); + + const pageFiles = await fg(`${pagesPath}/**/*.{astro,ts,js}`); + + const routes = ( + await Promise.all( + pageFiles.map(async (filePath) => { + const isPrerendered = await isRoutePrerendered(filePath); + const index = filePath.indexOf('pages/') + 6; + const routeSegment = filePath + .substring(index) + .replace(/\.(astro|ts|js)/, '') + .replace(/index$/, ''); + + /** + * @TODO + * figure out how to run `getStaticPaths` here. + */ + const isDynamicRoute = routeSegment.endsWith(']'); + const shouldIndex = !isDynamicRoute && !isPrerendered; + + return shouldIndex ? `${config.site}/${routeSegment}` : undefined; + }) + ) + ).filter((route): route is string => Boolean(route)); + const opts = validateOptions(config.site, options); + + opts.customPages = opts.customPages + ? Array.from(new Set([...routes, ...opts.customPages])) + : routes; + options = opts; + + logger.info(`build is starting + ${JSON.stringify(opts.customPages, null, 2)}`); + }, + 'astro:build:done': async ({ dir, pages }) => { try { + if (!config.site) { + return; + } + const opts = validateOptions(config.site, options); const { filter, customPages, serialize, entryLimit } = opts; @@ -97,14 +153,7 @@ const createPlugin = (options?: SitemapOptions): AstroIntegration => { } if (pageUrls.length === 0) { - // offer suggestion for SSR users - if (config.output !== 'static') { - logger.warn( - `No pages found! We can only detect sitemap routes for "static" builds. Since you are using an SSR adapter, we recommend manually listing your sitemap routes using the "customPages" integration option.\n\nExample: \`sitemap({ customPages: ['https://example.com/route'] })\`` - ); - } else { - logger.warn(`No pages found!\n\`${OUTFILE}\` not created.`); - } + logger.warn(`No pages found!\n\`${OUTFILE}\` not created.`); return; } diff --git a/packages/integrations/sitemap/src/utils/is-route-prerendered.ts b/packages/integrations/sitemap/src/utils/is-route-prerendered.ts new file mode 100644 index 0000000000000..468e09ba8489e --- /dev/null +++ b/packages/integrations/sitemap/src/utils/is-route-prerendered.ts @@ -0,0 +1,8 @@ +const REGEX_PRERENDER = /const prerender = true/g; +import { readFile } from 'fs/promises'; + +export async function isRoutePrerendered(filePath: string) { + const contents = await readFile(filePath, 'utf-8'); + + return REGEX_PRERENDER.test(contents); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e4d21e374715e..f9d63834682ca 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3495,11 +3495,13 @@ importers: astro: workspace:* astro-scripts: workspace:* chai: ^4.3.6 + fast-glob: ^3.2.11 mocha: ^9.2.2 sitemap: ^7.1.1 xml2js: 0.4.23 zod: ^3.17.3 dependencies: + fast-glob: 3.2.12 sitemap: 7.1.1 zod: 3.20.6 devDependencies: