UNPKG

html-pages

Version:

Simple development HTTP Server for file serving and directory listing made by a Designer. Use it for hacking your HTML/JavaScript/CSS files but not for deploying your final site.

325 lines (275 loc) 9.82 kB
// Native const path = require('path'); // Packages const fs = require('fs-extra'); const commandLineArgs = require('command-line-args'); const commandLineUsage = require('command-line-usage'); const chalk = require('chalk'); const _ = require('lodash'); const camelCase = require('camelcase'); const camelcaseKeys = require('camelcase-keys'); const validator = require('validator'); // Ours const pkg = require('../package'); module.exports = () => { const argsOptsFilePath = path.normalize( path.join(__dirname, '/../json/args.json') ); const jsonArgs = fs.readJsonSync(argsOptsFilePath); const convertIgnoreArgsToArray = (obj) => { if (!_.isUndefined(obj.ignore)) { if (_.isString(obj.ignore)) { obj.ignore = obj.ignore.split(','); } else if (!_.isArray(obj.ignore)) { obj.ignore = []; } } else { obj.ignore = []; } return obj; }; if (!_.isObject(jsonArgs.options)) { console.error(`${chalk.red('Error!')} Arguments file is not available!`); process.exit(1); } let optionsDefault = {}; let optionsUserHome = {}; let optionsArgs = {}; const optionDefinitions = _.chain(jsonArgs.options) .sortBy(arg => { return _.toLower(arg.name); }) .map(arg => { const option = {}; option.name = arg.name; if (_.isString(arg.alias)) { option.alias = arg.alias; } option.description = arg.description; option.show = arg.show !== false; arg.type = _.isString(arg.type) ? arg.type : 'String'; // eslint-disable-next-line no-eval option.type = eval(arg.type); option.typeLabel = _.isString(arg.typeLabel) ? arg.typeLabel : _.toLower(arg.type); option.typeLabel = option.typeLabel !== 'boolean' ? chalk.italic.dim(' <' + option.typeLabel + '> ') : ''; if (_.isBoolean(arg.defaultOption) && arg.defaultOption === true) { option.defaultOption = true; } if (_.isBoolean(arg.multiple) && arg.multiple === true) { option.multiple = true; } if (!_.isUndefined(arg.defaultValue)) { option.defaultValue = arg.defaultValue; option.description += ' (defaults to ' + option.defaultValue + ')'; } else { option.defaultValue = arg.type === 'Boolean' ? false : ''; } optionsDefault[option.name] = option.defaultValue; return option; }) .value(); const userHomeDir = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME']; const userConfigPath = path.join(userHomeDir, '.html-pages.json'); if (fs.existsSync(userConfigPath)) { try { optionsUserHome = fs.readJsonSync(userConfigPath); } catch (err) { console.error( `${chalk.red('Error!')} ${err}` ); process.exit(1); } if (!_.isObject(optionsUserHome)) { console.error( `${chalk.red('Error!')} The ~/.html-pages.json is defined but the content isn't an object. Please check it!` ); process.exit(1); } if (_.isString(optionsUserHome.ignore)) { optionsUserHome.ignore = optionsUserHome.ignore.split(','); } } _.each(commandLineArgs(optionDefinitions, { partial: true }), function (item, key) { if (!_.isUndefined(optionsDefault[key]) && optionsDefault[key] !== item) { optionsArgs[key] = item; } if (key === 'ignore') { if (_.isArray(item)) { optionsArgs[key] = item.join(',').split(','); } } if (key === '_unknown') { optionsArgs[key] = item; } }); if (_.isUndefined(optionsArgs._unknown)) { optionsArgs._unknown = []; } if (_.isArray(optionsArgs.root) && _.size(optionsArgs.root) !== 0) { _.merge(optionsArgs._unknown, _.slice(optionsArgs.root, 1)); optionsArgs.root = _.first(optionsArgs.root); } if (_.size(optionsArgs._unknown) !== 0) { console.error( chalk.red('Ups!') + ' The option "' + _.first(optionsArgs._unknown) + '" is unknown. Here\'s a list of all available options:' ); // Display help section optionsArgs.help = true; } // Delete unknown options delete optionsArgs._unknown; const appWorker = chalk.yellow.bold(pkg.name); optionsDefault = camelcaseKeys(convertIgnoreArgsToArray(optionsDefault)); optionsUserHome = camelcaseKeys(convertIgnoreArgsToArray(optionsUserHome)); optionsArgs = camelcaseKeys(convertIgnoreArgsToArray(optionsArgs)); if (!_.isUndefined(optionsArgs.layout) && !_.isString(optionsArgs.layout)) { optionsArgs.layout = false; } const options = _.merge({}, optionsDefault, optionsUserHome, optionsArgs); _.each(options, (item, key) => { const opt = _.find(jsonArgs.options, (item) => { return camelCase(item.name) === key; }); let itemBeforeFixed = false; if (_.isObject(opt) && _.isString(opt.validation)) { opt.typeLabel = _.isString(opt.typeLabel) ? opt.typeLabel : _.toLower(opt.type); const defaultValue = !_.isUndefined(opt.defaultValue) ? opt.defaultValue : (!_.isUndefined(opt.type) && _.toLower(opt.type) === 'string' ? '' : false); if (_.isFunction(validator[opt.validation])) { if (!validator[opt.validation](item + '')) { itemBeforeFixed = item; item = defaultValue; } } else if (_.isFunction(_[opt.validation])) { if (!_[opt.validation](item)) { itemBeforeFixed = item; item = defaultValue; } } else if (opt.validation === 'isFileName') { } else if (opt.validation === 'isHostname') { if (_.isEmpty(item) || _.isNull(item) || _.isUndefined(item)) { itemBeforeFixed = item; item = opt.defaultValue; } else { const isHost = new RegExp('^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]*[a-zA-Z0-9])\\.)*' + '([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\\-]*[A-Za-z0-9])$'); if (!isHost.test(item)) { console.error(chalk.red('Error!') + ' The host ' + chalk.bold.yellow(item) + ' is not valid.'); process.exit(1); } } } } if (opt.typeLabel === 'string') { item = _.escape(item); } else if (opt.typeLabel === 'array') { item = _.map(item, value => { return _.escape(value); }); } if (itemBeforeFixed !== false) { console.log(chalk.yellow('Warning!') + ' The value "' + chalk.blue.bold(itemBeforeFixed) + '" is not valid for option "' + chalk.blue.bold(opt.name) + '". The default value "' + chalk.blue.bold(item) + '" will be used.'); } options[key] = item; }); // Shows the helper menu if (options.help) { const usage = commandLineUsage([{ header: chalk.yellow(pkg.title), content: chalk.dim(pkg.description) }, { header: chalk.yellow('Synopsis'), content: 'Usage: ' + appWorker + chalk.dim(' <path> [options] [command]') }, { header: chalk.yellow('Options'), optionList: _.filter(optionDefinitions, arg => { return arg.show === true; }) }, { header: chalk.yellow('Examples'), content: [{ desc: '$ ' + appWorker + chalk.dim(' --port 1904'), example: '1. Start using another port.' }, { desc: '$ ' + appWorker + chalk.dim(' --directory-index=""'), example: '2. Disable the directory index file. It will always show the directory listings.' }, { desc: '$ ' + appWorker + chalk.dim(' --open'), example: '3. Launch default browser.' }, { desc: '$ ' + appWorker + chalk.dim(' --browser="chrome"'), example: '4. Use Google Chrome instead of default browser.' }, { desc: '$ ' + appWorker + chalk.dim(' --no-listing --no-cache'), example: '5. Hide the directory structure and disable cache.' }] }, { content: 'Project home: ' + chalk.yellow.underline(pkg.homepage) + '' }]); console.info(usage); process.exit(0); } // Shows the application version if (options.version) { console.info(pkg.version); process.exit(0); } // Merge ignore arrays options.ignore = [].concat(optionsDefault.ignore, optionsUserHome.ignore, optionsArgs.ignore); options.ignore = _.chain(options.ignore) .filter(item => { return !_.isEmpty(item); }) .map(item => { return _.trim(item); }) .uniqBy() .value(); if (!_.includes(['grid', 'list', false], options.layout)) { options.layout = 'grid'; } // Validate options if (!_.isString(options.directoryIndex) || _.includes(['false', ''], _.toLower(options.directoryIndex))) { options.directoryIndex = ''; } if (options.cache === 0) { options.noCache = true; } else if (options.noCache === true) { options.cache = 0; } // Don't log anything to the console if silent mode is enabled otherside if verbose // is set will show every log if (options.silent) { options.notifications = false; options.logLevel = 'silent'; options.verbose = false; console.log = () => {}; } else if (options.verbose) { options.logLevel = 'debug'; options.silent = false; } options.logLevel = _.toLower(options.logLevel); // If the user set a browser it will always open it if (!_.isEmpty(options.browser) && options.browser !== false) { options.open = true; } if (options.localhost !== false) { options.host = '127.0.0.1'; } options.host = _.toLower(options.host); options.onlyLocalhost = _.includes(['127.0.0.1', 'localhost'], options.host); // If the app is running a test if (options.dryTest) { options.open = false; options.notifications = false; options.noClipboard = true; } // Save the options object in a global variable global.options = options; return options; };