All files / src/Site SiteConfig.ts

96.77% Statements 30/31
90.9% Branches 40/44
100% Functions 2/2
96.77% Lines 30/31

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137        3x                                                                                                                                               18x     18x 18x 18x   18x 18x     18x   18x   18x 18x 18x 18x 18x 18x 18x   18x 18x   18x 18x 18x 18x 18x     18x     18x                       18x 18x 18x 18x   18x              
import fs from 'fs-extra';
import path from 'path';
import { FrontMatter } from '../plugins/Plugin.js';
 
const HEADING_INDEXING_LEVEL_DEFAULT = 3;
 
export type SiteConfigPage = {
  glob?: string,
  layout?: string,
  src?: string[],
  title?: string,
  externalScripts?: string[],
  globExclude?: string,
  searchable?: string | boolean,
  frontmatter?: FrontMatter,
  fileExtension?: string,
};
 
export type SiteConfigStyle = {
  bootstrapTheme?: string;
  darkMode: boolean;
  codeTheme: 'dark' | 'light';
  codeLineNumbers: boolean; // Default hide display of line numbers for code blocks
};
 
/**
 * Represents a read only site config read from the site configuration file,
 * with default values for unspecified properties.
 */
export class SiteConfig {
  baseUrl: string;
  enableSearch: boolean;
  faviconPath?: string;
  headingIndexingLevel: number;
 
  style: SiteConfigStyle;
 
  pages: SiteConfigPage[];
  pagesExclude: string[];
  ignore: string[];
 
  externalScripts: string[];
  titlePrefix: string;
  titleSuffix: string;
  globalOverride: Record<string, string>; // key: frontmatter key
 
  timeZone: string;
  locale: string;
 
  plugins: string[];
  pluginsContext: {
    [pluginName: string]: Record<string, any>
  };
 
  deploy: {
    message?: string;
    repo?: string;
    branch?: string;
    baseDir?: string;
  };
 
  intrasiteLinkValidation: {
    enabled: boolean;
  };
 
  plantumlCheck: boolean;
 
  pagefind?: {
    exclude_selectors?: string[];
  };
 
  /**
   * @param siteConfigJson The raw json read from the site configuration file
   * @param cliBaseUrl As read from the --baseUrl option
   */
  constructor(siteConfigJson: Record<string, any>, cliBaseUrl?: string) {
    this.baseUrl = cliBaseUrl !== undefined
      ? cliBaseUrl
      : (siteConfigJson.baseUrl || '');
    this.enableSearch = siteConfigJson.enableSearch === undefined || siteConfigJson.enableSearch;
    this.faviconPath = siteConfigJson.faviconPath;
    this.headingIndexingLevel = siteConfigJson.headingIndexingLevel || HEADING_INDEXING_LEVEL_DEFAULT;
 
    this.style = siteConfigJson.style || {};
    this.style.codeTheme = (this.style.codeTheme === 'dark' || this.style.codeTheme === 'light')
      ? this.style.codeTheme
      : 'dark';
    this.style.codeLineNumbers = this.style.codeLineNumbers !== undefined
      ? this.style.codeLineNumbers : false;
    this.style.darkMode = this.style.darkMode === true;
 
    this.pages = siteConfigJson.pages || [];
    this.pagesExclude = siteConfigJson.pagesExclude || [];
    this.ignore = siteConfigJson.ignore || [];
    this.externalScripts = siteConfigJson.externalScripts || [];
    this.titlePrefix = siteConfigJson.titlePrefix || '';
    this.titleSuffix = siteConfigJson.titleSuffix || '';
    this.globalOverride = siteConfigJson.globalOverride || {};
 
    this.timeZone = siteConfigJson.timeZone || 'UTC';
    this.locale = siteConfigJson.locale || 'en-GB';
 
    this.plugins = siteConfigJson.plugins || [];
    this.pluginsContext = siteConfigJson.pluginsContext || {};
    this.deploy = siteConfigJson.deploy || {};
    this.intrasiteLinkValidation = siteConfigJson.intrasiteLinkValidation || {};
    this.intrasiteLinkValidation.enabled = this.intrasiteLinkValidation.enabled !== false;
 
    // TODO this should probably be in pluginsContext
    this.plantumlCheck = siteConfigJson.plantumlCheck !== undefined
      ? siteConfigJson.plantumlCheck : true; // check PlantUML's prerequisite by default
 
    this.pagefind = siteConfigJson.pagefind;
  }
 
  /**
   * Read and returns the site config from site.json, overwrites the default base URL
   * if it's specified by the user.
   *
   * @param rootPath The absolute path to the site folder
   * @param siteConfigPath The relative path to the siteConfig
   * @param baseUrl user defined base URL (if exists)
   */
  static async readSiteConfig(rootPath: string, siteConfigPath: string, baseUrl?: string): Promise<any> {
    try {
      const absoluteSiteConfigPath = path.join(rootPath, siteConfigPath);
      const siteConfigJson = fs.readJsonSync(absoluteSiteConfigPath);
      const siteConfig = new SiteConfig(siteConfigJson, baseUrl);
 
      return siteConfig;
    } catch (err) {
      throw (new Error(`Failed to read the site config file '${siteConfigPath}' at`
        + `${rootPath}:\n${(err as Error).message}\nPlease ensure the file exist or is valid`));
    }
  }
}