UNPKG

rjweb-server

Version:

Easy and Robust Way to create a Web Server with Many Easy-to-use Features in NodeJS

373 lines (372 loc) 14.1 kB
#! /usr/bin/env node "use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const Logger_1 = require("./classes/Logger"); const index_1 = require("./index"); const child_process_1 = require("child_process"); const https_1 = __importDefault(require("https")); const yargs_1 = __importDefault(require("yargs")); const posix_1 = __importDefault(require("path/posix")); const path_1 = __importDefault(require("path")); const fs_1 = __importDefault(require("fs")); class Spinner { constructor() { this.current = 0; this.spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏']; } get() { this.current = (this.current + 1) % this.spinner.length; return this.spinner[this.current]; } } const prefix = `⚡ ${Logger_1.colors.fg.white}[RJWEB ${index_1.version.split('.')[0]}]${Logger_1.colors.fg.gray}:${Logger_1.colors.reset}`; const getAllOptionKeys = (options) => { return Object.keys(options).map((key) => { if (typeof options[key] === 'object' && !Array.isArray(options[key])) return getAllOptionKeys(options[key]).map((k) => `${key}.${k}`); else return key; }).flat(); }; const resolveOptionKey = (key) => { const parts = key.split('.'); let options = index_1.defaultOptions; for (const part of parts) { options = options[part]; } return options; }; const optionKeyValueToObject = (keys) => { let output = {}; for (const key in keys) { const parts = key.split('.'); let current = output; for (let i = 0; i < parts.length; i++) { if (i === parts.length - 1) { current[parts[i]] = keys[key]; } else { if (!current[parts[i]]) current[parts[i]] = {}; current = current[parts[i]]; } } } return output; }; const coloredPath = (path) => { let output = []; for (const part of path.split('/')) { output.push(`${Logger_1.colors.fg.blue}${part}`); } return output.join(`${Logger_1.colors.fg.cyan}/`); }; const pR = (location) => { return path_1.default.join(process.cwd(), location); }; const isX = (type, path) => { let infos; try { infos = fs_1.default.statSync(path); } catch { return false; } if (type === 'dir') return infos.isDirectory(); else return infos.isFile(); }; yargs_1.default .scriptName('rjweb') .usage('$0 <command> [args]') .version(index_1.version) .command('serve <folder>', 'Serve a Folder', // @ts-ignore ((cmd) => { cmd .positional('folder', { type: 'string', description: 'The Folder to serve', demandOption: true }) .option('runtime', { type: 'string', description: 'The Runtime Package to use', alias: ['r'], default: '@rjweb/runtime-node', demandOption: true }) .option('stripHtmlEnding', { type: 'boolean', description: 'Strip the .html Ending from Files', alias: ['s'], default: true }); for (const key of getAllOptionKeys(index_1.defaultOptions)) { if (key === 'version') continue; cmd.option(key, { description: `The ${key} Option`, type: typeof resolveOptionKey(key) === 'object' ? 'array' : typeof resolveOptionKey(key) }); } return cmd; }), (async (args) => { if (!isX('dir', pR(args.folder ?? '//'))) return console.error(`${prefix} ${Logger_1.colors.fg.red}Could not find ${Logger_1.colors.fg.cyan}${args.folder}`); const serverOptions = getAllOptionKeys(index_1.defaultOptions).reduce((acc, key) => { acc[key] = args[key] ?? resolveOptionKey(key); return acc; }, {}); console.log(`${prefix} ${Logger_1.colors.fg.gray}Starting Server...`); const server = new index_1.Server(await Promise.resolve(`${args.runtime}`).then(s => __importStar(require(s))).then((runtime) => runtime.Runtime).catch(() => { console.error(`${prefix} ${Logger_1.colors.fg.red}Could not find Runtime Package ${Logger_1.colors.fg.cyan}${args.runtime} ${Logger_1.colors.fg.red}installed globally.`); process.exit(1); }), optionKeyValueToObject(serverOptions)); server.path('/', (path) => path .static(pR(args.folder), { stripHtmlEnding: args.stripHtmlEnding })); server.http((ctr) => { console.log(`${prefix} ${Logger_1.colors.fg.gray}${ctr.client.ip.usual()} ${Logger_1.colors.fg.green}HTTP ${ctr.url.method} ${Logger_1.colors.fg.cyan}${coloredPath(ctr.url.path)}`); }); server.start() .then((port) => { console.log(`${prefix} ${Logger_1.colors.fg.green}Started on Port ${Logger_1.colors.fg.cyan}${port}`); }) .catch((err) => { console.error(`${prefix} ${Logger_1.colors.fg.red}An Error occured while starting the Server:`); console.error(`${Logger_1.colors.fg.cyan}${err.stack}`); process.exit(1); }); })) .command('generate <folder>', 'Generate a template Project', ((cmd) => cmd .positional('folder', { type: 'string', description: 'The Folder to generate the template in', demandOption: true }) .option('template', { type: 'string', description: 'Which template to use', alias: ['E'], default: 'choose', }) .option('variant', { type: 'string', description: 'The Variant of the template to use', alias: ['V'], default: 'choose', })), (async (args) => { const { default: inquirer } = await eval('import("inquirer")'); console.log(`${prefix} ${Logger_1.colors.fg.gray}Fetching Templates from GitHub...`); const templates = []; JSON.parse((await new Promise((resolve, reject) => { const chunks = []; https_1.default.get({ path: '/repos/0x7d8/NPM_WEB-SERVER/contents/templates', host: 'api.github.com', port: 443, headers: { "User-Agent": `rjweb-server@cli ${index_1.version}`, "Accept": 'application/vnd.github.v3+json', } }, (res) => { res.on('data', (data) => { chunks.push(data); }).once('error', reject) .once('end', () => { resolve(Buffer.concat(chunks)); }); }); })).toString()).filter((t) => t.type === 'dir').forEach((template) => { const variant = template.name.match(/\[.*\] /)[0].replace(/\[|\]/g, '').trim(); const name = template.name.replace(/\[.*\] /, ''); if (templates.some((t) => t.name === name)) { const index = templates.findIndex((t) => t.name === name); templates[index].variants.push({ name: variant, git: template }); } else { templates.push({ name, variants: [ { name: variant, git: template } ] }); } }); let template = '', variant = ''; if (args.template !== 'choose' && templates.some((t) => t.name === args.template)) { template = args.template; console.log(`${prefix} ${Logger_1.colors.fg.gray}Using ${Logger_1.colors.fg.cyan}${template}`); } else { if (args.template !== 'choose') console.log(`${prefix} ${Logger_1.colors.fg.cyan}${args.template} ${Logger_1.colors.fg.red}is not a valid template!`); await inquirer.prompt([ { name: 'Template', type: 'list', prefix, choices: templates.map((t) => t.name), askAnswered: true } ]).then((answers) => { template = answers.Template; }); } if (args.variant !== 'choose' && templates.some((t) => t.name === args.variant)) { variant = args.variant; console.log(`${prefix} ${Logger_1.colors.fg.gray}Using Variant ${Logger_1.colors.fg.cyan}${variant}`); } else { if (args.variant !== 'choose') console.log(`${prefix} ${Logger_1.colors.fg.cyan}${args.template} ${Logger_1.colors.fg.red}is not a valid template!`); await inquirer.prompt([ { name: 'Variant', type: 'list', prefix, choices: templates.find((t) => t.name === template).variants.map((v) => v.name), askAnswered: true } ]).then((answers) => { variant = answers.Variant; }); } if (!fs_1.default.existsSync(path_1.default.join(process.cwd(), args.folder))) { await fs_1.default.promises.mkdir(path_1.default.join(process.cwd(), args.folder)); } console.log(`${prefix} ${Logger_1.colors.fg.gray}Generating Template Project...`); const handleDirectory = async (directory) => { const files = JSON.parse((await new Promise((resolve, reject) => { const chunks = []; https_1.default.get({ path: new URL(directory).pathname, host: 'api.github.com', port: 443, headers: { "User-Agent": `rjweb-server@cli ${index_1.version}`, "Accept": 'application/vnd.github.v3+json', } }, (res) => { res.on('data', (data) => { chunks.push(data); }).once('error', reject) .once('end', () => { resolve(Buffer.concat(chunks)); }); }); })).toString()); if (Array.isArray(files)) { for (const file of files) { if (file.type === 'dir') { if (!fs_1.default.existsSync(posix_1.default.join(process.cwd(), args.folder, file.path.replace(`templates/[${variant}] ${template}`, '')))) { await fs_1.default.promises.mkdir(posix_1.default.join(process.cwd(), args.folder, file.path.replace(`templates/[${variant}] ${template}`, ''))); } } await handleDirectory(file.url); } } else { const file = files; if (file.name === 'yarn.lock') return; console.log(`${prefix} ${Logger_1.colors.fg.green}Downloaded ${Logger_1.colors.fg.cyan}${path_1.default.join(args.folder, file.path.replace(`templates/[${variant}] ${template}`, ''))}`); await fs_1.default.promises.writeFile(path_1.default.join(process.cwd(), args.folder, file.path.replace(`templates/[${variant}] ${template}`, '')), Buffer.from(file.content, 'base64').toString()); } }; await handleDirectory(templates.find((t) => t.name === template).variants.find((v) => v.name === variant).git.url); console.log(''); console.log(`${prefix} ${Logger_1.colors.fg.green}Template Project Generated!`); console.log(''); // Test for Package Managers let availablePackageManagers = []; try { (0, child_process_1.execSync)('npm --version', { stdio: 'ignore' }); availablePackageManagers.push('npm'); } catch { } try { (0, child_process_1.execSync)('yarn --version', { stdio: 'ignore' }); availablePackageManagers.push('yarn'); } catch { } try { (0, child_process_1.execSync)('pnpm --version', { stdio: 'ignore' }); availablePackageManagers.push('pnpm'); } catch { } try { (0, child_process_1.execSync)('bun --version', { stdio: 'ignore' }); availablePackageManagers.push('bun'); } catch { } let continueWith = 'npm'; await inquirer.prompt([ { name: 'Continue with', type: 'list', prefix, choices: availablePackageManagers, askAnswered: true } ]).then((answers) => { continueWith = answers['Continue with']; }); const spinner = new Spinner(); const runInterval = () => { process.stdout.write(`\r${prefix} ${Logger_1.colors.fg.yellow}${spinner.get()} ${Logger_1.colors.fg.gray}Installing Dependencies with ${Logger_1.colors.fg.cyan}${continueWith}${Logger_1.colors.fg.gray}...`); }; const interval = setInterval(runInterval, 175); runInterval(); process.chdir(path_1.default.join(process.cwd(), args.folder)); (0, child_process_1.exec)(`${continueWith} install`, () => { clearInterval(interval); process.stdout.write('\n'); console.log(`${prefix} ${Logger_1.colors.fg.green}Installed Dependencies!${Logger_1.colors.reset}`); }); })) .help() .argv;