# All files / src/Layout Layout.js

12.24% Statements 6/49
0% Branches 0/6
0% Functions 0/9
12.76% Lines 6/47

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
    1043x

    3x
    3x

    3x

    3x

    3x

     const { v4: uuidv4 } = require('uuid');

    const { PageSources } = require('../Page/PageSources');
    const { NodeProcessor } = require('../html/NodeProcessor');

    const logger = require('../utils/logger');

    const LAYOUT_PAGE_BODY_VARIABLE = 'content';

    class Layout {
      constructor(sourceFilePath, config) {
        this.sourceFilePath = sourceFilePath;
        this.config = config;

        this.includedFiles = new Set([this.sourceFilePath]);
        this.layoutProcessed = '';
        this.layoutPageBodyVariable = '';
        this.layoutPageNavUuid = '';
        this.headTop = [];
        this.headBottom = [];
        this.scriptBottom = [];
        this.layoutUserScriptsAndStyles = [];

        this.generatePromise = undefined;
      }

      shouldRegenerate(filePaths) {
        return filePaths.some(filePath => this.includedFiles.has(filePath));
      }

      async generate() {
        let triesLeft = 10;
        let numBodyVars;
        let pageSources;
        let nodeProcessor;

        do {
          const fileConfig = {
            ...this.config,
            headerIdMap: {},
          };
          pageSources = new PageSources();
          this.layoutPageBodyVariable = `{{${uuidv4()}-${uuidv4()}}}`;
          const layoutVariables = { [LAYOUT_PAGE_BODY_VARIABLE]: this.layoutPageBodyVariable };

          const { variableProcessor, pluginManager, siteLinkManager } = this.config;

          const nunjucksProcessed = variableProcessor.renderWithSiteVariables(
            this.sourceFilePath, pageSources, layoutVariables);
          nodeProcessor = new NodeProcessor(fileConfig, pageSources, variableProcessor,
                                            pluginManager, siteLinkManager, this.layoutUserScriptsAndStyles,
                                            'layout');
          // eslint-disable-next-line no-await-in-loop
          this.layoutProcessed = await nodeProcessor.process(this.sourceFilePath, nunjucksProcessed,
                                                             this.sourceFilePath, layoutVariables);
          this.layoutPageNavUuid = nodeProcessor.pageNavProcessor.getUuid();

          this.layoutProcessed = pluginManager.postRender(nodeProcessor.frontmatter, this.layoutProcessed);

          const pageBodyVarRegex = new RegExp(this.layoutPageBodyVariable, 'g');
          const bodyVarMatch = this.layoutProcessed.match(pageBodyVarRegex);
          numBodyVars = bodyVarMatch ? bodyVarMatch.length : 0;

          triesLeft -= 1;
        } while (triesLeft > 0 && numBodyVars > 1);

        if (triesLeft === 0) {
          logger.error(
            `Layout ${this.sourceFilePath} uses more than one {{ ${LAYOUT_PAGE_BODY_VARIABLE} }} variable.`);
          return;
        }

        this.headTop = nodeProcessor.headTop;
        this.headBottom = nodeProcessor.headBottom;
        this.scriptBottom = nodeProcessor.scriptBottom;

        pageSources.addAllToSet(this.includedFiles);
        await this.config.externalManager.generateDependencies(pageSources.getDynamicIncludeSrc(),
                                                               this.includedFiles);
      }

      insertPage(pageContent, pageNav, pageIncludedFiles) {
        this.includedFiles.forEach(filePath => pageIncludedFiles.add(filePath));

        // Use function for .replace, in case string contains special patterns (e.g. $$, $&, $1, ...)
        return this.layoutProcessed
          .replace(this.layoutPageBodyVariable, () => pageContent)
          .replace(this.layoutPageNavUuid, () => pageNav);
      }

      getPageNjkAssets() {
        return {
          headTop: this.headTop,
          headBottom: this.headBottom,
          scriptBottom: this.scriptBottom,
          layoutUserScriptsAndStyles: this.layoutUserScriptsAndStyles,
        };
      }
    }

    module.exports = {
      Layout,
    };