convert spreadsheet | filter data sheet based on url | google sheet layout template | Search

The provided code appears to be part of a template rendering engine that processes Google Sheets data, utilizing functions from the importer object to load, process, and render templates. It includes functions like getTemplateProperties and matchSections that recursively process template properties and sections, respectively.

Run example

npm run import -- "google sheet template properties"

google sheet template properties

var importer = require('../Core');
var getDataSheet = importer.import("google sheet array objects");
var renderRows = importer.import("google sheet layout template");
var getRows = importer.import("get worksheet rows");
var {filteredData, unfilteredData} = importer.import("filter data sheet based on url");
var promiseOrResolve = importer.import("resolve promise property");

var isWrapper = rows => rows.length === 1 && rows[0].length === 1
              && rows[0][0].match(/\{\{\s*>\s*(.*?)(\/(.*?-)+link)*\}\}/ig);

var isFiltered = (url) => url.split('/').length > 1

// TODO: remove previous properties if for some strange reason recalling the same template,
//   prevents situations like ::stylesheet being added to many times
var addedProperties = {};

function getTemplateProperties(key, properties, templates) {
    if(typeof templates[key] === 'undefined') {
        throw new Error(`section "${key}" not found!`)
    }
    // load template data
    var rows;
    return promiseOrResolve(templates[key].data, 'rows', getDataSheet)
        .then(data => {
            properties[key + '-original-data'] = data;
            properties[key + '-data'] = unfilteredData.bind(null, key);
        })
        // load template layout
        .then(() => promiseOrResolve(templates[key].template, 'rows', getRows))
        .then(rs => {
            rows = rs || []
            return rows.flat()
              .reduce((p, c, j) => p.then(() => matchSections(c, properties, templates)), Promise.resolve())
        })
        // detect if this is just a wrapper template and don't render rows, do a direct replacement
        .then(() => isWrapper(rows) ? rows[0][0] : renderRows(key, rows, properties, templates))
        .then(template => (properties[key] = template))
}

// must do this up front so we can process all data
var matchSections = (cell, properties, templates) => importer
    .regexToArray(/\{\{\s*>\s*(.*?)\s*\}\}/ig, cell, 1)
    .reduce((promise, section) => promise
        .then(() => getTemplateProperties(section.split('/')[0], properties, templates))
        .then(() => isFiltered(section)
              ? createAssignFilter(section, properties)
              : Promise.resolve()),
            Promise.resolve())

var isCategorical = (data, key) => data.filter(row => row.hasOwnProperty(key)).length > 0;

function createAssignFilter(section, properties) {
    var key = section.split('/')[0];
    var match = section.split('/').slice(1).join('/');
    properties[section] = properties[key];
    
    // add a special partial for the filtered data
    var categorical = isCategorical(properties[key + '-original-data'] || [], key)
    // automatically wrap unique templates in a data section for accessing filtered properties
    if(!categorical && !properties[key].includes(`{{#${key}-data}}`)
        && typeof properties[key + '-original-data'] !== 'undefined') {
        properties[section] = `{{#${key}-data}}${properties[key]}{{/${key}-data}}`;
    }
    // run the filtered function instead of using the array
    properties[key + '-data'] = filteredData.bind(null, key, match, properties, categorical);
}

module.exports = getTemplateProperties;

What the code could have been:

// Import required modules
const { getDataSheet, renderRows, getRows, filteredData, unfilteredData } = require('../Core');

// Define a function to check if a row is a wrapper template
const isWrapper = rows => rows.length === 1 && rows[0].length === 1
    && rows[0][0].match(/\{\{\s*>\s*(.*?)(\/(.*?-)+link)*\}\}/ig);

// Define a function to check if a URL is filtered
const isFiltered = (url) => url.split('/').length > 1;

// Define a function to get template properties
const getTemplateProperties = async (key, properties, templates, getDataSheet, renderRows, getRows, filteredData, unfilteredData) => {
    if (!templates[key]) {
        throw new Error(`Section "${key}" not found!`);
    }
    
    // Load template data
    const data = await promiseOrResolve(templates[key].data, 'rows', getDataSheet);
    properties[key + '-original-data'] = data;
    properties[key + '-data'] = unfilteredData.bind(null, key);

    // Load template layout
    const rows = await promiseOrResolve(templates[key].template, 'rows', getRows);
    const flatRows = rows.flat();
    
    // Detect if this is just a wrapper template and don't render rows, do a direct replacement
    if (isWrapper(flatRows)) {
        return flatRows[0][0];
    }
    
    // Render rows
    const template = await renderRows(key, flatRows, properties, templates);
    properties[key] = template;
    
    return properties;
};

// Define a function to match sections
const matchSections = async (cell, properties, templates, getDataSheet, renderRows, getRows, filteredData, unfilteredData) => {
    const sections = importer.regexToArray(/\{\{\s*>\s*(.*?)\s*\}\}/ig, cell, 1);
    
    return sections.reduce(async (promise, section) => {
        const result = await promise;
        const key = section.split('/')[0];
        
        // Check if the section is filtered
        if (isFiltered(section)) {
            return createAssignFilter(section, properties);
        }
        
        // Get template properties
        const properties = await getTemplateProperties(key, properties, templates, getDataSheet, renderRows, getRows, filteredData, unfilteredData);
        
        return Promise.resolve(properties);
    }, Promise.resolve(properties));
};

// Define a function to create an assign filter
const createAssignFilter = (section, properties) => {
    const key = section.split('/')[0];
    const match = section.split('/').slice(1).join('/');
    properties[section] = properties[key];
    
    // Add a special partial for the filtered data
    const categorical = isCategorical(properties[key + '-original-data'] || [], key);
    
    // Automatically wrap unique templates in a data section for accessing filtered properties
    if (!categorical &&!properties[key].includes(`{{#${key}-data}}`) && typeof properties[key + '-original-data']!== 'undefined') {
        properties[section] = `{{#${key}-data}}${properties[key]}{{/${key}-data}}`;
    }
    
    // Run the filtered function instead of using the array
    properties[key + '-data'] = filteredData.bind(null, key, match, properties, categorical);
};

// Define a function to check if data is categorical
const isCategorical = (data, key) => data.filter(row => row.hasOwnProperty(key)).length > 0;

// Export the getTemplateProperties function
module.exports = async (key, properties, templates, getDataSheet, renderRows, getRows, filteredData, unfilteredData) => {
    return getTemplateProperties(key, properties, templates, getDataSheet, renderRows, getRows, filteredData, unfilteredData);
};

Code Breakdown

Variables and Functions

getTemplateProperties Function

This function retrieves template properties from a given key and updates the properties object. Here's a step-by-step breakdown:

  1. Error Handling: Checks if the template key exists in the templates object. If not, throws an error.
  2. Load Template Data: Uses promiseOrResolve to load the template data from the getDataSheet function.
  3. Update Properties: Binds the unfilteredData function to the key and stores the data in the properties object.
  4. Load Template Layout: Uses promiseOrResolve to load the template layout from the getRows function.
  5. Process Rows: Flattens the rows array and uses reduce to process each row. If the row matches a wrapper template, it returns the row as a single element. Otherwise, it renders the rows using the renderRows function.
  6. Update Properties: Stores the rendered template in the properties object.

matchSections Function

This function matches sections in a cell and recursively calls getTemplateProperties to process each section. Here's a step-by-step breakdown:

  1. Regex Extraction: Uses importer.regexToArray to extract sections from the cell.
  2. Reduce Function: Recursively calls getTemplateProperties for each section and returns a promise chain.

Other Functions

Context

The code appears to be part of a template rendering engine that processes Google Sheets data. It uses a combination of functions from the importer object to load and process data, update properties, and render templates.