<commit_before>'use strict';

var file = require('file'),
    fs = require('fs'),
    config = require('../../lib/config'),
    semver = require('semver'),
    path = require('path'),
    log = require('../logger');

function Parser() {}

Parser.prototype.parse = function(bundlePath) {
    var dir = 'bundles/' + bundlePath + '/';
    var manifestPath = dir + "nodecg.json";

    // Return null if nodecg.json doesn't exist
    if (!fs.existsSync(manifestPath)) {
        return null;
    }

    // Read metadata from the nodecg.json manifest file
    var bundle = this.readManifest(manifestPath);
    bundle.dir = dir;

    // Don't load the bundle if it depends on a different version of NodeCG
    if (this.checkVersion(bundle) === false) {
        return null;
    }

    // Read all HTML/CSS/JS files in the package's "admin" dir into memory
    // They will then get passed to dashboard.jade at the time of 'GET' and be templated into the final rendered page
    bundle.admin = {};
    bundle.admin.dir = bundle.dir + 'admin/';
    this.readAdminResources(bundle);

    bundle.view = {};
    bundle.view.url = 'http://' + config.host + ':' + config.port + '/view/' + bundle.name;

    return bundle;
};

Parser.prototype.readManifest = function(path) {
    // Parse the JSON from nodecg.json
    var manifest = JSON.parse(fs.readFileSync(path, 'utf8'));

    // Copy the JSON we use into the beginnings of our bundle object
    return {
        name: manifest.name,
        version: manifest.version,
        description: manifest.description,
        homepage: manifest.homepage,
        authors: manifest.authors,
        resolutions: manifest.resolutions,
        license: manifest.license,
        nodecgDependency: manifest.nodecgDependency,
        extension: manifest.extension,
        bundleDependencies: manifest.bundleDependencies
    };
};

Parser.prototype.readAdminResources = function(bundle) {
    // Arrays with the JS, CSS, and HTML
    bundle.admin.js = [];
    bundle.admin.css = [];
    bundle.admin.html = [];

    var adminDir = fs.readdirSync(bundle.admin.dir); // returns just the filenames of each file in the folder, not full path
    adminDir.forEach(function(file) {
        var filepath = bundle.admin.dir + file;
        if (!fs.statSync(filepath).isFile()) {
            // Skip directories
            return;
        }

        // Panels are strictly limited to HTML, JS, and CSS. No EJS, Jade, LESS, SASS, etc.
        switch (path.extname(filepath)) {
            case '.html':
                bundle.admin.html.push(fs.readFileSync(filepath, {encoding: 'utf8'}));<commit_msg>Don't crash if a bundle has no "admin" dir<commit_after>'use strict';

var file = require('file'),
    fs = require('fs'),
    config = require('../../lib/config'),
    semver = require('semver'),
    path = require('path'),
    log = require('../logger');

function Parser() {}

Parser.prototype.parse = function(bundlePath) {
    var dir = 'bundles/' + bundlePath + '/';
    var manifestPath = dir + "nodecg.json";

    // Return null if nodecg.json doesn't exist
    if (!fs.existsSync(manifestPath)) {
        return null;
    }

    // Read metadata from the nodecg.json manifest file
    var bundle = this.readManifest(manifestPath);
    bundle.dir = dir;

    // Don't load the bundle if it depends on a different version of NodeCG
    if (this.checkVersion(bundle) === false) {
        return null;
    }

    // Read all HTML/CSS/JS files in the package's "admin" dir into memory
    // They will then get passed to dashboard.jade at the time of 'GET' and be templated into the final rendered page
    bundle.admin = {};
    bundle.admin.dir = bundle.dir + 'admin/';
    this.readAdminResources(bundle);

    bundle.view = {};
    bundle.view.url = 'http://' + config.host + ':' + config.port + '/view/' + bundle.name;

    return bundle;
};

Parser.prototype.readManifest = function(path) {
    // Parse the JSON from nodecg.json
    var manifest = JSON.parse(fs.readFileSync(path, 'utf8'));

    // Copy the JSON we use into the beginnings of our bundle object
    return {
        name: manifest.name,
        version: manifest.version,
        description: manifest.description,
        homepage: manifest.homepage,
        authors: manifest.authors,
        resolutions: manifest.resolutions,
        license: manifest.license,
        nodecgDependency: manifest.nodecgDependency,
        extension: manifest.extension,
        bundleDependencies: manifest.bundleDependencies
    };
};

Parser.prototype.readAdminResources = function(bundle) {
    // Arrays with the JS, CSS, and HTML
    bundle.admin.js = [];
    bundle.admin.css = [];
    bundle.admin.html = [];

    if (!fs.existsSync(bundle.admin.dir)) {
        // Skip if 'admin' dir does not exist
        return;
    }

    var adminDir = fs.readdirSync(bundle.admin.dir); // returns just the filenames of each file in the folder, not full path
    adminDir.forEach(function(file) {
        var filepath = bundle.admin.dir + file;
        if (!fs.statSync(filepath).isFile()) {
            // Skip directories
            return;
        }

        // Panels are strictly limited to HTML, JS, and CSS. No EJS, Jade, LESS, SASS, etc.
        switch (path.extname(filepath)) {
            case '.html':
                bundle.admin.html.push(fs.readFileSync(filepath, {encoding: 'utf8'}));
{'fraction_non_alphanumeric': 0.12639405204460966, 'fraction_numerical': 0.0007434944237918215, 'mean_word_length': 2.577792553191489, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, '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': '7352321', 'n_tokens_mistral': 1559, 'n_tokens_neox': 1522, 'n_words': 449}