<commit_before>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else 
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else 
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                let pages = parsePages();
                for (index in pages)
                {
                    console.log(pages);

                    let page = pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    //@@TODO: write html out
                    console.log(html);
                }
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            console.log("File not found: " + path);
        }
        else 
        {
            console.log(err);
        }
    }
}

//
// Write out a ut8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            console.log("Path not found: " + path);
        }
        else 
        {
            console.log(err);
        }
    }
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        pages_path: "./pages",

        // style and structure information
        styles: ["./style.css"],
        templates: [
            // name:
            // styles:
            // 
        ],

        // optimization options
        client_markdown: false,
        
    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        //@@TODO: transform page contents
        //console.log(pages[page]);

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;
        while (cur != null)
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++; 
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' || 
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        pos++; 
                        cur = page.charAt(pos);
                    }

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    console.log(metadata);

                    console.log("YAML found, page " + index);
                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                console.log("no valid YAML, page " + index);
                break;
            }

            pos++;
            cur = page.charAt(pos);
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in properties)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;

        pageObjs.push(pageObj);
    }
    return pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        console.log(k + ", " + v);
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information.
//
function getTemplate(name)
{
    if (settings.templates[name] != null)
    {
        return settings.templates[name];
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
    }
}

//
// Generate HTML
//
function generateHTML(template, page)
{
    let html = '';
    function add(line)
    {
        html += line + '\n';
    }
    
    //@@TODO: add page contents

    add('<!DOCTYPE html>');
    add('<html>');
        //@@TODO: finish html header
        add('\t<head>');
            add('\t\t<title>' + page.title + '</title>')
            //@@TODO: add references to css and markdown parser as appropriate
        add('\t</head>');
        add('\t<body>');

        add('\t</body>');

    html += '</html>';
    return html;
}

function printHelp()
{
    console.log("Usage: node ossg.js [options] file");
    console.log("Oscar's static site generator. Compile templates, css, & posts into a website.");
}
<commit_msg>Add the start of the templating engine.<commit_after>const fs = require('fs');

let settings = {};

function main()
{
    let args = getArguments();
    if (args.length < 1)
    {
        printHelp();
    }
    else 
    {
        if (args[0] == '-h' || args[0] == '-help' || args[0] == '--help')
        {
            printHelp();
        }
        else if (args[0] == '-V' || args[0] == '-version' || args[0] == '--version')
        {
            printVersion();
        }
        else 
        {
            let file = readFile(args[0]);
            if (file != null)
            {
                parseSettings(file);
                let pages = parsePages();
                for (index in pages)
                {
                    let page = pages[index];
                    let template = getTemplate(page.template);
                    let html = generateHTML(template, page);

                    //@@TODO: write html out
                    console.log(html);
                }
            }
        }
    }
}

//
// Get the command line arguments.
//
function getArguments()
{
    return process.argv.slice(2);
}

//
// Read in a utf8-encoded file.
//
function readFile(path)
{
    let data = '';
    try
    {
        data = fs.readFileSync(path, 'utf8');
        return data;
    }
    catch (err)
    {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "File not found: " + path;
        }
        else 
        {
            console.log(err);
        }
    }
}

//
// Write out a ut8-encoded file.
//
function writeFile(path, data)
{
    fs.writeFileSync(path, data);
}

//
// Get the names of files in a folder
//
function getFilenames(path)
{
    try {
        return fs.readdirSync(path);
    }
    catch (err) {
        if (err.errno == -2)
        {
            //@@TODO: throw a proper error.
            throw "Path not found: " + path;
        }
        else 
        {
            console.log(err);
        }
    }
}

//
// Parse the settings JSON file.
//
function parseSettings(data)
{
    let json = JSON.parse(data);
    const properties = {
        // paths
        input_path: ".",
        output_path: ".",
        pages_path: "./pages",

        // style and structure information
        styles: ["./style.css"],
        templates: [
            // name:
            // styles:
            // 
        ],

        // optimization options
        client_markdown: false,
        
    };

    for (property in properties)
    {
        if (json[property])
        {
            settings[property] = json[property];
        }
        else
        {
            settings[property] = properties[property];
        }
    }
}

//
// Parse the pages.
// Return page objects, which have metadata properties and a content property.
//
function parsePages()
{
    let filenames = getFilenames(settings.pages_path);
    let pages = [];
    let pageObjs = [];

    for (file in filenames)
    {
        pages.push(readFile(settings.pages_path + '/' + filenames[file]));
    }

    for (index in pages)
    {
        let page = pages[index];
        let pageObj = {};
        //@@TODO: transform page contents
        //console.log(pages[page]);

        // extract the metadata from the front of the file
        let pos = 0;
        let cur = page.charAt(pos);
        let metadata;

        function advance()
        {
            pos++; 
            cur = page.charAt(pos);
        }

        while (cur != null && cur != '')
        {
            if (cur == '-')
            {
                if (page.charAt(pos + 1) == '-' &&
                    page.charAt(pos + 2) == '-')
                {
                    pos++; 
                    cur = page.charAt(pos);
                    metadata = '';

                    while (cur != '-' || 
                        page.charAt(pos + 1) != '-' ||
                        page.charAt(pos + 2) != '-')
                    {
                        metadata += cur;
                        advance();
                    }

                    // trim the front:
                    metadata = metadata.slice(2);
                    if (metadata.charAt(0) == '\n' || metadata.charAt(0) == '\r')
                    {
                        metadata = metadata.slice(1);
                    }

                    break;
                }
            }
            else if (cur != '\n' && cur != ' ' && cur != '\t' && cur != '\r')
            {
                //@@TODO: Emit a better error (and stop program?)
                console.log("no valid YAML, page " + index);
                break;
            }

            advance();
        }

        let dataObj = {};
        if (metadata)
        {
            dataObj = parseMetadata(metadata);
        }

        // defaults
        const properties = {
            title: null,
            template: null,
            // ...
        };

        // copy the dataObj properties into the pageObj.
        for (property in properties)
        {
            if (dataObj[property])
            {
                pageObj[property] = dataObj[property];
            }
            else
            {
                pageObj[property] = properties[property];
            }
        }

        pageObj.content = page;

        pageObjs.push(pageObj);
    }
    return pageObjs;
}

//
// Parse YAML metadata.
//
function parseMetadata(metadata)
{
    let lines = metadata.split('\n');
    let dataObj = {};
    for (i in lines)
    {
        let line = lines[i];
        line = line.trim();
        if (line == '') continue; // skip blank lines
        let separator = line.indexOf(':');
        let k = line.slice(0, separator);
        let v = line.substr(separator + 1);
        k = k.trim();
        v = v.trim();
        dataObj[k] = v;
    }
    return dataObj;
}

//
// Get template information.
//
function getTemplate(name)
{
    let template = settings.templates.find(function (e) {
        return e.name == name;
    });

    if (template != null)
    {
        template.html = readFile(settings.input_path + '/' + template.path);
    }
    else
    {
        //@@TODO: throw error and exit if template not found.
        throw 'No template called ' + name + ' found.';
    }

    return template;
}

//
// Generate HTML.
// A basic templating engine.
//
function generateHTML(template, page)
{
    // we use a template scope when executing template code.
    let scope = {};
    scope.page = page;
    scope.template = template;

    console.log(scope)

    let html = template.html;
    let output = '';
    let pos = 0;
    let cur = html.charAt(pos);
    let next = html.charAt(pos + 1);

    function advance()
    {
        pos++;
        cur = html.charAt(pos);
        next = html.charAt(pos + 1);
    }

    //@@TODO: throw proper error.
    if (html.length == 0) throw "Template " + template.name + " has no content.";

    while (cur != null && cur != '')
    {
        if (cur == '{' && next == '{')
        {
            let start = pos;
            let templateString = '';

            while (cur != '}' || next != '}')
            {
                templateString += cur;
                advance();
            }
            let length = pos - start;
            // advance past the '}}'.
            advance();
            advance(); 

            // trim front
            templateString = templateString.slice(2);
            let templateOutput;
            try {
                //@@TODO: eval is evil!!!!111
                templateOutput = eval("scope." + templateString);
            }
            catch (err)
            {   
                //@@TODO: proper error message.
                console.log("Property " + templateString + " not found.");
            }

            if (templateOutput != null)
            {
                output += templateOutput;
            }
            console.log(templateString);
        }

        output += cur;
        advance();
    }

    return output;
}

function printHelp()
{
    console.log("Usage: node ossg.js [options] file");
    console.log("Oscar's static site generator. Compile templates, css, & posts into a website.");
}

{'fraction_non_alphanumeric': 0.13738920225624496, 'fraction_numerical': 0.00389470856835885, 'mean_word_length': 1.2145724907063198, 'pattern_counts': {'":': 0, '<': 14, '<?xml version=': 0, '>': 12, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6548127', 'n_tokens_mistral': 4287, 'n_tokens_neox': 4111, 'n_words': 1023}