123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180 |
- import { SitemapBuilder } from './sitemap-builder.js';
- import path from 'node:path';
- import { generateUrl } from '../utils/url.js';
- import { combineMerge } from '../utils/merge.js';
- import { RobotsTxtBuilder } from './robots-txt-builder.js';
- import { defaultRobotsTxtTransformer } from '../utils/defaults.js';
- import { exportFile } from '../utils/file.js';
- export class ExportableBuilder {
- exportableList = [];
- config;
- runtimePaths;
- sitemapBuilder;
- robotsTxtBuilder;
- exportDir;
- constructor(config, runtimePaths) {
- this.config = config;
- this.runtimePaths = runtimePaths;
- this.sitemapBuilder = new SitemapBuilder();
- this.robotsTxtBuilder = new RobotsTxtBuilder();
- this.exportDir = path.resolve(process.cwd(), this.config.outDir);
- }
- /**
- * Register sitemap index files
- */
- async registerIndexSitemap() {
- // Get generated sitemap list
- const sitemaps = [
- ...this.generatedSitemaps(),
- // Include additionalSitemaps provided via robots.txt options
- ...(this.config?.robotsTxtOptions?.additionalSitemaps ?? []),
- ];
- // Generate sitemap-index content
- const content = this.sitemapBuilder.buildSitemapIndexXml(sitemaps);
- // Create exportable
- const item = {
- type: 'sitemap-index',
- filename: this.runtimePaths.SITEMAP_INDEX_FILE,
- url: this.runtimePaths.SITEMAP_INDEX_URL,
- content,
- };
- // Add to exportable list
- this.exportableList.push(item);
- }
- /**
- * Resolve filename if index sitemap is generated
- * @param index
- * @returns
- */
- resolveFilenameWithIndexSitemap(index) {
- return `${this.config.sitemapBaseFileName}-${index}.xml`;
- }
- /**
- * Resolve filename if index sitemaps is not generated
- * @param index
- * @returns
- */
- resolveFilenameWithoutIndexSitemap(index) {
- if (index === 0) {
- return `${this.config.sitemapBaseFileName}.xml`;
- }
- return this.resolveFilenameWithIndexSitemap(index);
- }
- /**
- * Register sitemaps with exportable builder
- * @param chunks
- */
- async registerSitemaps(chunks) {
- // Check whether user config allows sitemap generation
- const hasIndexSitemap = this.config.generateIndexSitemap;
- // Create exportable items
- const items = chunks?.map((chunk, index) => {
- // Get sitemap base filename
- const baseFilename = hasIndexSitemap
- ? this.resolveFilenameWithIndexSitemap(index)
- : this.resolveFilenameWithoutIndexSitemap(index);
- return {
- type: 'sitemap',
- url: generateUrl(this.config.siteUrl, baseFilename),
- filename: path.resolve(this.exportDir, baseFilename),
- content: this.sitemapBuilder.buildSitemapXml(chunk),
- };
- });
- // Add to exportable list
- this.exportableList.push(...items);
- }
- /**
- * Get robots.txt export config
- * @returns
- */
- robotsTxtExportConfig() {
- // Endpoints list
- const endpoints = [];
- // Include non-index sitemaps
- // Optionally allow user to include non-index sitemaps along with generated sitemap list
- // Set to true if index-sitemap is not generated
- const includeNonIndexSitemaps = this.config.generateIndexSitemap
- ? this.config?.robotsTxtOptions?.includeNonIndexSitemaps
- : true;
- // Add all sitemap indices
- if (this.config.generateIndexSitemap) {
- endpoints.push(...this.generatedSitemapIndices());
- }
- // Add all non-index sitemaps
- if (includeNonIndexSitemaps) {
- endpoints.push(...this.generatedSitemaps());
- }
- // Combine merge with additional sitemaps
- return combineMerge({
- robotsTxtOptions: {
- additionalSitemaps: endpoints,
- },
- }, this.config);
- }
- /**
- * Register robots.txt export
- */
- async registerRobotsTxt() {
- // File name of robots.txt
- const baseFilename = 'robots.txt';
- // Export config of robots.txt
- const robotsConfig = this.robotsTxtExportConfig();
- // Generate robots content
- let content = this.robotsTxtBuilder.generateRobotsTxt(robotsConfig);
- // Get robots transformer
- const robotsTransformer = robotsConfig?.robotsTxtOptions?.transformRobotsTxt ??
- defaultRobotsTxtTransformer;
- // Transform generated robots txt
- content = await robotsTransformer(robotsConfig, content);
- // Generate exportable item
- const item = {
- type: 'robots.txt',
- filename: path.resolve(this.exportDir, baseFilename),
- url: generateUrl(robotsConfig?.siteUrl, baseFilename),
- content,
- };
- // Add to exportableList
- this.exportableList.push(item);
- }
- /**
- * Generic reducer to extract by type
- * @param condition
- * @returns
- */
- exportableUrlReducer(condition) {
- return this.exportableList.reduce((prev, curr) => {
- const matches = condition(curr);
- if (matches) {
- prev.push(curr.url);
- }
- return prev;
- }, []);
- }
- /**
- * Return a lit of sitemap urls
- * @returns
- */
- generatedSitemaps() {
- return this.exportableUrlReducer((x) => x.type == 'sitemap');
- }
- /**
- * Generate sitemap indices
- * @returns
- */
- generatedSitemapIndices() {
- return this.exportableUrlReducer((x) => x.type == 'sitemap-index');
- }
- /**
- * Export all registered files
- * @returns
- */
- async exportAll() {
- await Promise.all(this.exportableList?.map(async (item) => exportFile(item.filename, item.content)));
- // Create result object
- return {
- runtimePaths: this.runtimePaths,
- sitemaps: this.generatedSitemaps(),
- sitemapIndices: this.generatedSitemapIndices(),
- };
- }
- }
|