/** * Download roblox types command */ import { Argv, CommandModule } from 'yargs'; import { OutputHelper } from '@quenty/cli-output-helpers'; import { NevermoreGlobalArgs } from '../../args/global-args.js'; import * as fs from 'fs/promises'; import * as fsSync from 'fs'; import * as https from 'https'; export interface DownloadRobloxTypesArgs extends NevermoreGlobalArgs { fileName?: string; } /** * Creates a new game with Nevermore dependencies */ export class DownloadRobloxTypes implements CommandModule { public command = 'download-roblox-types [file-name]'; public describe = 'Downloads the Roblox Luau type definitions.'; public builder = (args: Argv) => { args.positional('file-name', { describe: 'Path to save the downloaded Roblox Luau type definitions.', demandOption: false, type: 'string', default: 'globalTypes.d.lua', }); return args as Argv; }; public handler = async (args: DownloadRobloxTypesArgs) => { const filename = args.fileName ?? 'globalTypes.d.lua'; await DownloadRobloxTypes._download( filename, 'https://raw.githubusercontent.com/JohnnyMorganz/luau-lsp/main/scripts/globalTypes.d.lua' ); }; private static async _download(filename: string, url: string): Promise { if (!(await DownloadRobloxTypes._needsDownload(filename))) { OutputHelper.verbose(`Skipping download, ${filename} is up to date`); return; } OutputHelper.verbose( `Downloading Roblox type definitions to ${filename}...` ); return new Promise((resolve, reject) => { const file = fsSync.createWriteStream(filename); const request = https.get(url, (response: any) => { if (response.statusCode !== 200) { fsSync.unlink(filename, () => {}); reject( new Error(`Failed to download ${url}: HTTP ${response.statusCode}`) ); return; } response.pipe(file); file.on('finish', () => { file.close(); OutputHelper.verbose(`Downloaded ${filename}`); resolve(); }); }); request.on('error', (err: Error) => { fsSync.unlink(filename, () => {}); reject(err); }); file.on('error', (err: Error) => { fsSync.unlink(filename, () => {}); reject(err); }); }); } private static async _needsDownload(filename: string): Promise { try { // Check age of file const stats = await fs.stat(filename); const oneDayInMs = 24 * 60 * 60 * 1000; const fileAge = Date.now() - stats.mtimeMs; if (fileAge > oneDayInMs) { return true; } return false; } catch (error: any) { // File doesn't exist if (error.code === 'ENOENT') { return true; } throw error; } } }